////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxnode.cpp
/// @brief Node class implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxchicontext.h"
#include "camxcmdbuffermanager.h"
#include "camxcsl.h"
#include "camxhal3defs.h"
#include "camxhal3metadatautil.h"
#include "camxhal3metadatautil.h"
#include "camxhal3module.h"
#include "camxhal3types.h"
#include "camxhwcontext.h"
#include "camxhwdefs.h"
#include "camxhwenvironment.h"
#include "camxhwfactory.h"
#include "camximagebuffermanager.h"
#include "camximagedump.h"
#include "camximageformatutils.h"
#include "camxincs.h"
#include "camxmem.h"
#include "camxmetadatapool.h"
#include "camxnode.h"
#include "camxpropertyblob.h"
#include "camxpropertydefs.h"
#include "camxsettingsmanager.h"
#include "camxthreadmanager.h"
#include "camxvendortags.h"
#include "parametertuningtypes.h"

#include "chi.h"

CAMX_NAMESPACE_BEGIN

CAMX_TLS_STATIC_CLASS_DEFINE(UINT64, Node, m_tRequestId, FirstValidRequestId);

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::Node
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Node::Node()
{
    m_maxPipelineDelay = GetStaticSettings()->maxPipelineDelay;
    m_parallelProcessRequests = FALSE;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::~Node
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Node::~Node()
{
    if ((0 != m_numCmdBufferManagers) && (NULL != m_ppCmdBufferManagers))
    {
        for (UINT i = 0; i < m_numCmdBufferManagers; i++)
        {
            CAMX_ASSERT(NULL != m_ppCmdBufferManagers[i]);
            if (NULL != m_ppCmdBufferManagers[i])
            {
                CAMX_DELETE m_ppCmdBufferManagers[i];
                m_ppCmdBufferManagers[i] = NULL;
            }
        }
    }

    if (NULL != m_ppCmdBufferManagers)
    {
        CAMX_FREE(m_ppCmdBufferManagers);
        m_numCmdBufferManagers    = 0;
        m_maxNumCmdBufferManagers = 0;
        m_ppCmdBufferManagers     = NULL;
    }

    if (NULL != m_pProcessRequestLock)
    {
        m_pProcessRequestLock->Destroy();
        m_pProcessRequestLock = NULL;
    }

    if (NULL != m_pBufferRequestLock)
    {
        m_pBufferRequestLock->Destroy();
        m_pBufferRequestLock = NULL;
    }

    if (NULL != m_pBufferReleaseLock)
    {
        m_pBufferReleaseLock->Destroy();
        m_pBufferReleaseLock = NULL;
    }

    if (NULL != m_pFenceCreateReleaseLock)
    {
        m_pFenceCreateReleaseLock->Destroy();
        m_pFenceCreateReleaseLock = NULL;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::Create(
    const NodeCreateInputData* pCreateInputData,
    NodeCreateOutputData*      pCreateOutputData)
{
    CAMX_ENTRYEXIT_NAME(CamxLogGroupCore, "NodeCreate");
    CamxResult result = CamxResultSuccess;

    const HwFactory* pFactory = HwEnvironment::GetInstance()->GetHwFactory();
    Node*            pNode    = pFactory->CreateNode(pCreateInputData, pCreateOutputData);

    if (pNode != NULL)
    {
        result = pNode->Initialize(pCreateInputData, pCreateOutputData);

        if (CamxResultSuccess != result)
        {
            pNode->Destroy();
            pNode = NULL;
        }
    }
    else
    {
        result = CamxResultENoMemory;
    }

    pCreateOutputData->pNode = pNode;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::Destroy
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::Destroy()
{
    if ((m_outputPortsData.numPorts > 0) && (NULL != m_bufferNegotiationData.pOutputPortNegotiationData))
    {
        CAMX_FREE(m_bufferNegotiationData.pOutputPortNegotiationData);
        m_bufferNegotiationData.pOutputPortNegotiationData = NULL;
    }

    if ((m_inputPortsData.numPorts > 0) && (NULL != m_bufferNegotiationData.pInputPortNegotiationData))
    {
        CAMX_FREE(m_bufferNegotiationData.pInputPortNegotiationData);
        m_bufferNegotiationData.pInputPortNegotiationData = NULL;
    }

    for (UINT i = 0; i < m_outputPortsData.numPorts; i++)
    {
        CamxResult          result         = CamxResultSuccess;
        OutputPort*         pOutputPort    = &m_outputPortsData.pOutputPorts[i];
        ImageBufferManager* pBufferManager = NULL;

        // release any fences
        if ((NULL != pOutputPort) && (NULL != pOutputPort->pFenceHandlerData))
        {
            m_pFenceCreateReleaseLock->Lock();
            for (UINT bufferIndex = 0; bufferIndex < pOutputPort->bufferProperties.maxImageBuffers; bufferIndex++)
            {
                if (CSLInvalidHandle != pOutputPort->pFenceHandlerData[bufferIndex].hFence)
                {
                    result = CSLReleaseFence(pOutputPort->pFenceHandlerData[bufferIndex].hFence);

                    CAMX_LOG_DRQ("Released Fence Output...Node: %08x, id: %s:%d, fence: %08x(%d),"
                        " ImgBuf:%p reqId:%llu result: %d ",
                        this, m_pNodeName, InstanceID(), &pOutputPort->pFenceHandlerData[bufferIndex],
                        pOutputPort->pFenceHandlerData[bufferIndex].hFence, pOutputPort->ppImageBuffers[0], m_tRequestId,
                        result);

                    pOutputPort->pFenceHandlerData[bufferIndex].hFence = CSLInvalidHandle;
                }

                if (CSLInvalidHandle != pOutputPort->pDelayedBufferFenceHandlerData[bufferIndex].hFence)
                {
                    result = CSLReleaseFence(pOutputPort->pDelayedBufferFenceHandlerData[bufferIndex].hFence);

                    CAMX_LOG_DRQ("Released Fence.Output..Node: %08x, id: %s:%d, fence: %08x(%d),"
                        " ImgBuf:%p reqId:%llu result: %d ",
                        this, m_pNodeName, InstanceID(), &pOutputPort->pDelayedBufferFenceHandlerData[bufferIndex],
                        pOutputPort->pDelayedBufferFenceHandlerData[bufferIndex].hFence, pOutputPort->ppImageBuffers[0],
                        m_tRequestId, result);

                    pOutputPort->pDelayedBufferFenceHandlerData[bufferIndex].hFence = CSLInvalidHandle;
                }
            }
            m_pFenceCreateReleaseLock->Unlock();
        }

        m_pBufferReleaseLock->Lock();
        if (NULL != pOutputPort)
        {
            if (NULL != pOutputPort->pImageBufferManager)
            {
                pBufferManager = pOutputPort->pImageBufferManager;
                result = pBufferManager->ReleaseBuffers(FALSE);

                CAMX_ASSERT(CamxResultSuccess == result);

                pBufferManager->Destroy();
                pBufferManager = NULL;
            }
            else
            {
                if (NULL != pOutputPort->ppImageBuffers)
                {
                    for (UINT bufferIndex = 0; bufferIndex < pOutputPort->bufferProperties.maxImageBuffers; bufferIndex++)
                    {
                        if (NULL != pOutputPort->ppImageBuffers[bufferIndex])
                        {
                            CAMX_DELETE pOutputPort->ppImageBuffers[bufferIndex];
                            pOutputPort->ppImageBuffers[bufferIndex] = NULL;
                        }
                    }
                }
            }
        }
        m_pBufferReleaseLock->Unlock();

        if ((NULL != pOutputPort) && (NULL != pOutputPort->pFenceHandlerData))
        {
            CAMX_FREE(pOutputPort->pFenceHandlerData);
            pOutputPort->pFenceHandlerData = NULL;
        }

        if ((NULL != pOutputPort) && (NULL != pOutputPort->pDelayedBufferFenceHandlerData))
        {
            CAMX_FREE(pOutputPort->pDelayedBufferFenceHandlerData);
            pOutputPort->pDelayedBufferFenceHandlerData = NULL;
        }

        if ((NULL != pOutputPort) && (NULL != pOutputPort->ppImageBuffers))
        {
            CAMX_FREE(pOutputPort->ppImageBuffers);
            pOutputPort->ppImageBuffers = NULL;
        }
    }

    ImageBufferManager* pInputPortBufferManager = NULL;
    for (UINT i = 0; i < m_inputPortsData.numPorts; i++)
    {
        InputPort* pInputPort   = &m_inputPortsData.pInputPorts[i];
        pInputPortBufferManager = pInputPort->pImageBufferManager;

        if ((TRUE == IsSourceBufferInputPort(i)) && (NULL != pInputPort->ppImageBuffers))
        {
            if (NULL != pInputPortBufferManager)
            {
                pInputPortBufferManager->ReleaseBuffers(FALSE);
                pInputPortBufferManager->Destroy();
                pInputPort->pImageBufferManager = NULL;
            }
            else
            {
                for (UINT bufferIndex = 0; bufferIndex < MaxRequestQueueDepth; bufferIndex++)
                {
                    if (NULL != pInputPort->ppImageBuffers[bufferIndex])
                    {
                        CAMX_DELETE pInputPort->ppImageBuffers[bufferIndex];
                        pInputPort->ppImageBuffers[bufferIndex] = NULL;
                    }
                }

                CAMX_FREE(pInputPort->ppImageBuffers);
                pInputPort->ppImageBuffers = NULL;
            }

            m_pFenceCreateReleaseLock->Lock();
            if (NULL != pInputPort->phFences)
            {
                for (UINT bufferIndex = 0; bufferIndex < MaxRequestQueueDepth; bufferIndex++)
                {
                    if (CSLInvalidHandle != pInputPort->phFences[bufferIndex])
                    {
                        CamxResult result = CSLReleaseFence(pInputPort->phFences[bufferIndex]);

                        CAMX_LOG_DRQ("Released Fence.Input.Node: %08x, id: %s:%d, fence: %08x(%d), reqId:%llu result: %d ",
                            this, m_pNodeName, InstanceID(), &pInputPort->phFences[bufferIndex],
                            pInputPort->phFences[bufferIndex], m_tRequestId, result);

                        pInputPort->phFences[bufferIndex] = CSLInvalidHandle;
                    }
                }

                CAMX_FREE(pInputPort->phFences);
                pInputPort->phFences = NULL;
            }
            m_pFenceCreateReleaseLock->Unlock();
        }
    }

    if (NULL != m_outputPortsData.pOutputPorts)
    {
        CAMX_FREE(m_outputPortsData.pOutputPorts);
        m_outputPortsData.pOutputPorts = NULL;
    }

    if (NULL != m_inputPortsData.pInputPorts)
    {
        CAMX_FREE(m_inputPortsData.pInputPorts);
        m_inputPortsData.pInputPorts = NULL;
    }

    for (UINT index = 0; index < MaxRequestQueueDepth; index++)
    {
        if (NULL != m_perRequestInfo[index].activePorts.pInputPorts)
        {
            CAMX_FREE(m_perRequestInfo[index].activePorts.pInputPorts);
            m_perRequestInfo[index].activePorts.pInputPorts = NULL;
        }

        if (NULL != m_perRequestInfo[index].activePorts.pOutputPorts)
        {
            CAMX_FREE(m_perRequestInfo[index].activePorts.pOutputPorts);
            m_perRequestInfo[index].activePorts.pOutputPorts = NULL;
        }
    }

    if (TRUE == HwEnvironment::GetInstance()->GetStaticSettings()->watermarkImage)
    {
        if (NULL != m_pWatermarkPattern)
        {
            CAMX_FREE(m_pWatermarkPattern);
            m_pWatermarkPattern = NULL;
        }
    }

    CAMX_DELETE this;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ProcessingNodeInitialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::ProcessingNodeInitialize(
    const NodeCreateInputData* pCreateInputData,
    NodeCreateOutputData*      pCreateOutputData)
{
    CAMX_UNREFERENCED_PARAM(pCreateInputData);
    CAMX_UNREFERENCED_PARAM(pCreateOutputData);

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::InitializeNonSinkPortBufferProperties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::InitializeNonSinkPortBufferProperties(
    UINT            outputPortIndex,
    const PortLink* pPortLink)
{
    if (Sensor != Type())
    {
        OutputPort*                 pOutputPort           = &m_outputPortsData.pOutputPorts[outputPortIndex];
        const LinkBufferProperties* pLinkBufferProperties = &pPortLink->linkBufferProperties;

        pOutputPort->flags.isBatchMode                              = pPortLink->linkProperties.isBatchMode;
        pOutputPort->bufferProperties.immediateAllocImageBuffers    = pLinkBufferProperties->immediateAllocCount;
        pOutputPort->bufferProperties.maxImageBuffers               = pLinkBufferProperties->queueDepth;
        // numBatchedFrames=1 is the default value - the real value will be known when we finalize the node initialization
        // at the time the pipeline is bound to the session
        pOutputPort->numBatchedFrames                    = 1;
        pOutputPort->bufferProperties.imageFormat.format = static_cast<Format>(pLinkBufferProperties->format);

        if (pLinkBufferProperties->sizeBytes > 0)
        {
            pOutputPort->bufferProperties.imageFormat.width  = pLinkBufferProperties->sizeBytes;
            pOutputPort->bufferProperties.imageFormat.height = 1;
        }

        switch (pLinkBufferProperties->heap)
        {
            case BufferHeapDSP:
                pOutputPort->bufferProperties.bufferHeap = CSLBufferHeapDSP;
                break;
            case BufferHeapSystem:
                pOutputPort->bufferProperties.bufferHeap = CSLBufferHeapSystem;
                break;
            case BufferHeapEGL:
                pOutputPort->bufferProperties.bufferHeap = CSLBufferHeapEGL;
                break;
            case BufferHeapIon:
                pOutputPort->bufferProperties.bufferHeap = CSLBufferHeapIon;
                break;
            default:
                CAMX_ASSERT_ALWAYS_MESSAGE("Invalid buffer heap (%d) coming from XML", pLinkBufferProperties->heap);
                break;
        }

        for (UINT i = 0; i < pLinkBufferProperties->numMemFlags; i++)
        {
            switch (pLinkBufferProperties->memFlags[i])
            {
                case BufferMemFlag::Cache:
                    pOutputPort->bufferProperties.memFlags |= CSLMemFlagCache;
                    break;
                case BufferMemFlag::Hw:
                    pOutputPort->bufferProperties.memFlags |= CSLMemFlagHw;
                    break;
                case BufferMemFlag::Protected:
                    pOutputPort->bufferProperties.memFlags |= CSLMemFlagProtected;
                    break;
                case BufferMemFlag::CmdBuffer:
                    pOutputPort->bufferProperties.memFlags |= CSLMemFlagCmdBuffer;
                    break;
                case BufferMemFlag::UMDAccess:
                    pOutputPort->bufferProperties.memFlags |= CSLMemFlagUMDAccess;
                    break;
                case BufferMemFlag::PacketBuffer:
                    pOutputPort->bufferProperties.memFlags |= CSLMemFlagPacketBuffer;
                    break;
                case BufferMemFlag::KMDAccess:
                    pOutputPort->bufferProperties.memFlags |= CSLMemFlagKMDAccess;
                    break;
                default:
                    CAMX_ASSERT_ALWAYS_MESSAGE("Invalid buffer flags (flags=%d) coming from XML",
                                               pLinkBufferProperties->memFlags[i]);
                    break;
            }
        }

        ///@ todo (CAMX-346) Handle it properly
        pOutputPort->bufferProperties.imageFormat.colorSpace = CamX::ColorSpace::BT601Full;
        pOutputPort->bufferProperties.imageFormat.rotation   = CamX::Rotation::CW0Degrees;
        pOutputPort->bufferProperties.imageFormat.alignment  = 1;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::InitializeSinkPortBufferProperties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::InitializeSinkPortBufferProperties(
    UINT                        outputPortIndex,
    const NodeCreateInputData*  pNodeCreateInputData,
    const OutputPortInfo*       pOutputPortInfo)
{
    CAMX_ASSERT(Sensor != Type());

    CAMX_UNREFERENCED_PARAM(pNodeCreateInputData);

    OutputPort*       pOutputPort       = &m_outputPortsData.pOutputPorts[outputPortIndex];
    ImageFormat*      pImageFormat      = &pOutputPort->bufferProperties.imageFormat;
    const PortLink*   pPortLink         = &pOutputPortInfo->portLink;
    ChiStreamWrapper* pChiStreamWrapper = m_pPipeline->GetOutputStreamWrapper(Type(), InstanceID(), pOutputPort->portId);
    ChiStream*        pChiStream        = NULL;

    if (NULL != pChiStreamWrapper)
    {
        pChiStream = reinterpret_cast<ChiStream*>(pChiStreamWrapper->GetNativeStream());

        pImageFormat->format = pChiStreamWrapper->GetInternalFormat();
        pImageFormat->width  = pChiStream->width;
        pImageFormat->height = pChiStream->height;

        CAMX_LOG_INFO(CamxLogGroupCore,
                      "Topology: %s:%d has a sink port id %d using format %d dim %dx%d",
                      m_pNodeName,
                      InstanceID(),
                      pOutputPort->portId,
                      pChiStreamWrapper->GetInternalFormat(),
                      pChiStream->width,
                      pChiStream->height);

        pOutputPort->flags.isBatchMode           = pPortLink->linkProperties.isBatchMode;
        pOutputPort->bufferProperties.memFlags   = CSLMemFlagHw;
        pOutputPort->bufferProperties.bufferHeap = CSLBufferHeapIon;

        // numBatchedFrames=1 is the default value - the real value will be known when we finalize the node initialization
        // at the time the pipeline is bound to the session
        pOutputPort->numBatchedFrames = 1;
        // We need to wrap the HAL output buffer with ImageBuffer*. Since there could a max of RequestQueueDepth requests
        // outstanding, we'll create a max of (MaxRequestQueueDepth * numBatchedFrames) ImageBuffer*
        pOutputPort->bufferProperties.maxImageBuffers = ((m_pPipeline->GetRequestQueueDepth() + 2) *
                                                         pOutputPort->numBatchedFrames);

        pImageFormat->alignment = 1;

        switch (pChiStream->dataspace)
        {
            case HALDataspaceUnknown:
                pImageFormat->colorSpace = ColorSpace::Unknown;
                break;
            case HALDataspaceJFIF:
            case HALDataspaceV0JFIF:
                pImageFormat->colorSpace = ColorSpace::BT601Full;
                break;
            case HALDataspaceBT601_625:
                pImageFormat->colorSpace = ColorSpace::BT601625;
                break;
            case HALDataspaceBT601_525:
                pImageFormat->colorSpace = ColorSpace::BT601525;
                break;
            case HALDataspaceBT709:
                pImageFormat->colorSpace = ColorSpace::BT709;
                break;
            case HALDataspaceDepth:
                pImageFormat->colorSpace = ColorSpace::Depth;
                break;
            default:
                /// @todo (CAMX-346) Add more colorspace here.
                pImageFormat->colorSpace = ColorSpace::Unknown;
                break;
        }

        switch (pChiStream->rotation)
        {
            case StreamRotationCCW0:
                pImageFormat->rotation = Rotation::CW0Degrees;
                break;
            case StreamRotationCCW90:
                pImageFormat->rotation = Rotation::CW90Degrees;
                break;
            case StreamRotationCCW180:
                pImageFormat->rotation = Rotation::CW180Degrees;
                break;
            case StreamRotationCCW270:
                pImageFormat->rotation = Rotation::CW270Degrees;
                break;
            default:
                pImageFormat->rotation = Rotation::CW0Degrees;
                break;
        }

        OutputPortNegotiationData* pPortNegotiationData = &m_bufferNegotiationData.pOutputPortNegotiationData[outputPortIndex];
        /// @note If an output port has a SINK destination (input port), it can be its only destination
        BufferRequirement*         pBufferRequirement   = &pPortNegotiationData->inputPortRequirement[0];

        pBufferRequirement->minWidth      = pChiStream->width;
        pBufferRequirement->minHeight     = pChiStream->height;
        pBufferRequirement->maxWidth      = pChiStream->width;
        pBufferRequirement->maxHeight     = pChiStream->height;
        pBufferRequirement->optimalWidth  = pChiStream->width;
        pBufferRequirement->optimalHeight = pChiStream->height;
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "pChiStreamWrapper is NULL");
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::InitializeSourcePortBufferFormat
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::InitializeSourcePortBufferFormat(
    UINT              inPortIndex,
    ChiStreamWrapper* pChiStreamWrapper)
{
    CAMX_ASSERT(Sensor != Type());

    InputPort*      pInputPort      = &m_inputPortsData.pInputPorts[inPortIndex];
    ImageFormat*    pImageFormat    = &pInputPort->imageFormat;
    ChiStream*      pChiStream      = reinterpret_cast<ChiStream*>(pChiStreamWrapper->GetNativeStream());
    FormatParamInfo formatParamInfo = {0};
    formatParamInfo.isHALBuffer     = TRUE; // Buffer was externally allocated
    formatParamInfo.isImplDefined   = ((pChiStream->format == ChiStreamFormatImplDefined) ||
                                       (pChiStream->format == ChiStreamFormatYCrCb420_SP)) ? TRUE : FALSE;

    formatParamInfo.grallocUsage    = pChiStream->grallocUsage;
    if (TRUE == GetStaticSettings()->IsStrideSettingEnable)
    {
        formatParamInfo.planeStride = pChiStream->streamParams.planeStride;
        formatParamInfo.sliceHeight = pChiStream->streamParams.sliceHeight;
    }
    else
    {
        formatParamInfo.planeStride = 0;
        formatParamInfo.sliceHeight = 0;
    }

    pImageFormat->format = pChiStreamWrapper->GetInternalFormat();
    pImageFormat->width  = pChiStream->width;
    pImageFormat->height = pChiStream->height;
    pImageFormat->alignment = 1;

    if ((Format::FDYUV420NV12 == pImageFormat->format) || (Format::FDY8 == pImageFormat->format))
    {
        UINT32 baseFDResolution = static_cast<UINT32>(GetStaticSettings()->FDBaseResolution);

        formatParamInfo.baseFDResolution.width  = baseFDResolution & 0xFFFF;
        formatParamInfo.baseFDResolution.height = (baseFDResolution >> 16) & 0xFFFF;
    }

    CAMX_LOG_INFO(CamxLogGroupCore,
                  "Topology: %s:%d has a source port id %d using format %d dim %dx%d",
                  m_pNodeName,
                  InstanceID(),
                  pInputPort->portId,
                  pChiStreamWrapper->GetInternalFormat(),
                  pChiStream->width,
                  pChiStream->height);

    ImageFormatUtils::InitializeFormatParams(pImageFormat, &formatParamInfo);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::InitializeNonSinkHALBufferPortBufferProperties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::InitializeNonSinkHALBufferPortBufferProperties(
    UINT                    outputPortIndex,
    const OutputPort*       pInplaceOutputPort,
    const ChiStreamWrapper* pChiStreamWrapper)
{
    CAMX_ASSERT(Sensor != Type());

    OutputPort*       pOutputPort  = &m_outputPortsData.pOutputPorts[outputPortIndex];
    ImageFormat*      pImageFormat = &pOutputPort->bufferProperties.imageFormat;
    ChiStream*        pChiStream   = NULL;

    if (NULL != pChiStreamWrapper)
    {
        pChiStream = reinterpret_cast<ChiStream*>(pChiStreamWrapper->GetNativeStream());

        /// @todo (CAMX-1797) Delete this function
        pImageFormat->format = pChiStreamWrapper->GetInternalFormat();
        pImageFormat->width  = pChiStream->width;
        pImageFormat->height = pChiStream->height;

        CAMX_LOG_INFO(CamxLogGroupCore,
            "Topology: %s:%d has a non sink HAL buffer port id %d using format %d dim %dx%d. pImageFormat = %p",
            m_pNodeName,
            InstanceID(),
            pOutputPort->portId,
            pChiStreamWrapper->GetInternalFormat(),
            pChiStream->width,
            pChiStream->height,
            pImageFormat);

        pOutputPort->flags.isBatchMode           = pInplaceOutputPort->flags.isBatchMode;
        pOutputPort->bufferProperties.memFlags   = CSLMemFlagHw;
        pOutputPort->bufferProperties.bufferHeap = CSLBufferHeapIon;

        // numBatchedFrames=1 is the default value - the real value will be known when we finalize the node initialization
        // at the time the pipeline is bound to the session
        pOutputPort->numBatchedFrames = 1;
        // We need to wrap the HAL output buffer with ImageBuffer*. Since there could a max of RequestQueueDepth requests
        // outstanding, we'll create a max of (MaxRequestQueueDepth * numBatchedFrames) ImageBuffer*
        pOutputPort->bufferProperties.maxImageBuffers = (MaxRequestQueueDepth * pOutputPort->numBatchedFrames);

        pImageFormat->alignment = 1;

        switch (pChiStream->dataspace)
        {
            case HALDataspaceUnknown:
                pImageFormat->colorSpace = ColorSpace::Unknown;
                break;
            case HALDataspaceJFIF:
            case HALDataspaceV0JFIF:
                pImageFormat->colorSpace = ColorSpace::BT601Full;
                break;
            case HALDataspaceBT601_625:
                pImageFormat->colorSpace = ColorSpace::BT601625;
                break;
            case HALDataspaceBT601_525:
                pImageFormat->colorSpace = ColorSpace::BT601525;
                break;
            case HALDataspaceBT709:
                pImageFormat->colorSpace = ColorSpace::BT709;
                break;
            case HALDataspaceDepth:
                pImageFormat->colorSpace = ColorSpace::Depth;
                break;
            default:
                /// @todo (CAMX-346) Add more colorspace here.
                pImageFormat->colorSpace = ColorSpace::Unknown;
                break;
        }

        switch (pChiStream->rotation)
        {
            case StreamRotationCCW0:
                pImageFormat->rotation = Rotation::CW0Degrees;
                break;
            case StreamRotationCCW90:
                pImageFormat->rotation = Rotation::CW90Degrees;
                break;
            case StreamRotationCCW180:
                pImageFormat->rotation = Rotation::CW180Degrees;
                break;
            case StreamRotationCCW270:
                pImageFormat->rotation = Rotation::CW270Degrees;
                break;
            default:
                pImageFormat->rotation = Rotation::CW0Degrees;
                break;
        }

        OutputPortNegotiationData* pPortNegotiationData = &m_bufferNegotiationData.pOutputPortNegotiationData[outputPortIndex];
        /// @note If an output port has a SINK destination (input port), it can be its only destination
        BufferRequirement*         pBufferRequirement = &pPortNegotiationData->inputPortRequirement[0];

        pBufferRequirement->minWidth      = pChiStream->width;
        pBufferRequirement->minHeight     = pChiStream->height;
        pBufferRequirement->maxWidth      = pChiStream->width;
        pBufferRequirement->maxHeight     = pChiStream->height;
        pBufferRequirement->optimalWidth  = pChiStream->width;
        pBufferRequirement->optimalHeight = pChiStream->height;

        pPortNegotiationData->numInputPortsNotification++;

        if (pPortNegotiationData->numInputPortsNotification == pOutputPort->numInputPortsConnected)
        {
            // When all the input ports connected to the output port have notified the output port, it means the
            // output port has all the buffer requirements it needs to make a decision for the buffer on that output
            // port
            m_bufferNegotiationData.numOutputPortsNotified++;
        }

        // m_outputPortsData.numSinkPorts++;
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "pChiStreamWrapper is NULL");
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::Initialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::Initialize(
    const NodeCreateInputData* pCreateInputData,
    NodeCreateOutputData*      pCreateOutputData)
{
    CamxResult         result          = CamxResultSuccess;
    const PerNodeInfo* pNodeCreateInfo = NULL;

    if ((NULL == pCreateInputData)              ||
        (NULL == pCreateInputData->pNodeInfo)   ||
        (NULL == pCreateInputData->pPipeline)   ||
        (NULL == pCreateInputData->pChiContext) ||
        (NULL == pCreateOutputData))
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "pCreateInputData, pCreateOutputData - %p, %p is NULL",
                       pCreateInputData, pCreateOutputData);
        result = CamxResultEInvalidArg;
    }

    if (CamxResultSuccess == result)
    {
        pNodeCreateInfo = pCreateInputData->pNodeInfo;

        m_nodeType = pNodeCreateInfo->nodeId;
        m_instanceId = pNodeCreateInfo->instanceId;
        m_maxjpegsize = 0;
        OsUtils::SNPrintF(m_nodeNameInstanceId, sizeof(m_nodeNameInstanceId), "%s%d", Name(), InstanceID());

        CAMX_TRACE_SYNC_BEGIN_F(CamxLogGroupCore, "NodeInitialize: %s", m_nodeNameInstanceId);

        m_pPipeline = pCreateInputData->pPipeline;
        m_pUsecasePool = m_pPipeline->GetPerFramePool(PoolType::PerUsecase);
        m_pipelineNodeIndex = pCreateInputData->pipelineNodeIndex;
        m_inputPortsData.numPorts = pNodeCreateInfo->inputPorts.numPorts;
        m_pHwContext = pCreateInputData->pChiContext->GetHwContext();
        m_pChiContext = pCreateInputData->pChiContext;


        if (NodeClass::Bypass == pNodeCreateInfo->nodeClass)
        {
            m_nodeFlags.isInplace = FALSE;
            pCreateOutputData->createFlags.isInPlace = FALSE;
            m_nodeFlags.isBypassable = TRUE;
            pCreateOutputData->createFlags.isBypassable = TRUE;
        }
        else if (NodeClass::Inplace == pNodeCreateInfo->nodeClass)
        {
            m_nodeFlags.isInplace = TRUE;
            pCreateOutputData->createFlags.isInPlace = TRUE;
            m_nodeFlags.isBypassable = FALSE;
            pCreateOutputData->createFlags.isBypassable = FALSE;
        }
        else
        {
            m_nodeFlags.isInplace = FALSE;
            m_nodeFlags.isBypassable = FALSE;
            pCreateOutputData->createFlags.isBypassable = FALSE;
            pCreateOutputData->createFlags.isInPlace = FALSE;
        }

        CAMX_LOG_INFO(CamxLogGroupCore, "Is Node %d(:%s):%d bypassable = %d inplace = %d",
            m_nodeType, m_pNodeName, m_instanceId, m_nodeFlags.isBypassable, m_nodeFlags.isInplace);

        if (NULL != pCreateInputData->pPipeline)
        {
            m_nodeFlags.isRealTime = pCreateInputData->pPipeline->IsRealTime();
            m_nodeFlags.isSecureMode = pCreateInputData->pPipeline->IsSecureMode();
        }

        if (0 != m_nodeExtComponentCount)
        {
            result = HwEnvironment::GetInstance()->SearchExternalComponent(&m_nodeExtComponents[0], m_nodeExtComponentCount);
        }

        if (m_inputPortsData.numPorts > 0)
        {
            m_inputPortsData.pInputPorts = static_cast<InputPort*>(CAMX_CALLOC(sizeof(InputPort) * m_inputPortsData.numPorts));
            m_bufferNegotiationData.pInputPortNegotiationData =
                static_cast<InputPortNegotiationData*>(
                    CAMX_CALLOC(sizeof(InputPortNegotiationData) * m_inputPortsData.numPorts));

            if ((NULL == m_inputPortsData.pInputPorts) || (NULL == m_bufferNegotiationData.pInputPortNegotiationData))
            {
                result = CamxResultENoMemory;
            }
        }
    }

    if (CamxResultSuccess == result)
    {
        m_outputPortsData.numPorts = pNodeCreateInfo->outputPorts.numPorts;

        if (m_outputPortsData.numPorts > 0)
        {
            m_outputPortsData.pOutputPorts =
                static_cast<OutputPort*>(CAMX_CALLOC(sizeof(OutputPort) * m_outputPortsData.numPorts));

            if (NULL == m_outputPortsData.pOutputPorts)
            {
                CAMX_ASSERT_ALWAYS_MESSAGE("Node::Initialize Cannot allocate memory for output ports");

                result = CamxResultENoMemory;
            }
            else
            {
                m_bufferNegotiationData.pOutputPortNegotiationData =
                    static_cast<OutputPortNegotiationData*>(
                        CAMX_CALLOC(sizeof(OutputPortNegotiationData) * m_outputPortsData.numPorts));

                if (NULL == m_bufferNegotiationData.pOutputPortNegotiationData)
                {
                    CAMX_ASSERT_ALWAYS_MESSAGE("Node::Initialize Cannot allocate memory for buffer negotiation data");

                    result = CamxResultENoMemory;
                }
            }
        }
    }
    else
    {
        CAMX_ASSERT_ALWAYS_MESSAGE("Node::Initialize Cannot allocate memory for input ports");

        result = CamxResultENoMemory;
    }

    if (CamxResultSuccess == result)
    {
        for (UINT outputPortIndex = 0; outputPortIndex < m_outputPortsData.numPorts; outputPortIndex++)
        {
            /// @todo (CAMX-359) - Have a set of utility functions to extract information from the Usecase structure instead
            ///                    of this multi-level-indirection

            const OutputPortInfo* pOutputPortCreateInfo = &pNodeCreateInfo->outputPorts.portInfo[outputPortIndex];
            OutputPort*           pOutputPort           = &m_outputPortsData.pOutputPorts[outputPortIndex];

            pOutputPort->portId                         = pOutputPortCreateInfo->portId;
            pOutputPort->numInputPortsConnected         = pOutputPortCreateInfo->portLink.numInputPortsConnected;
            pOutputPort->flags.isSecurePort             = m_nodeFlags.isSecureMode;
            pOutputPort->portSourceTypeId               = pOutputPortCreateInfo->portSourceTypeId;
            pOutputPort->numSourcePortsMapped           = pOutputPortCreateInfo->numSourceIdsMapped;
            pOutputPort->pMappedSourcePortIds           = pOutputPortCreateInfo->pMappedSourcePortIds;

            OutputPortNegotiationData* pOutputPortNegotiationData    =
                &m_bufferNegotiationData.pOutputPortNegotiationData[outputPortIndex];

            pOutputPortNegotiationData->outputPortIndex              = outputPortIndex;
            // This will be filled in by the derived node at the end of buffer negotiation
            pOutputPortNegotiationData->pFinalOutputBufferProperties = &pOutputPort->bufferProperties;

            /// @note If an output port has a SINK destination, it can be the only destination for that link
            if ((TRUE == pOutputPortCreateInfo->flags.isSinkBuffer) || (TRUE == pOutputPortCreateInfo->flags.isSinkNoBuffer))
            {
                m_outputPortsData.sinkPortIndices[m_outputPortsData.numSinkPorts] = outputPortIndex;

                if (TRUE == pOutputPortCreateInfo->flags.isSinkBuffer)
                {
                    ChiStreamWrapper* pChiStreamWrapper =
                        m_pPipeline->GetOutputStreamWrapper(Type(), InstanceID(), pOutputPort->portId);

                    if (NULL != pChiStreamWrapper)
                    {
                        pOutputPort->flags.isSinkBuffer = TRUE;
                        /// @todo (CAMX-1797) sinkTarget, sinkTargetStreamId - need to remove
                        pOutputPort->sinkTargetStreamId = pChiStreamWrapper->GetStreamIndex();

                        pCreateOutputData->createFlags.isSinkBuffer = TRUE;

                        CAMX_ASSERT(CamxInvalidStreamId != pOutputPort->sinkTargetStreamId);

                        pOutputPort->enabledInStreamMask = (1 << pOutputPort->sinkTargetStreamId);

                        InitializeSinkPortBufferProperties(outputPortIndex, pCreateInputData, pOutputPortCreateInfo);
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupCore, "pChiStreamWrapper for Port Index at %d is null", outputPortIndex);
                        result = CamxResultEInvalidPointer;
                        break;
                    }
                }
                else
                {
                    pCreateOutputData->createFlags.isSinkNoBuffer = TRUE;
                    pOutputPort->flags.isSinkNoBuffer             = TRUE;
                    /// @note SinkNoBuffer is enabled in all streams
                    pOutputPort->enabledInStreamMask              = ((1 << MaxNumStreams) - 1);
                }

                /// @note If an output port has a SINK destination (input port), it can be its only destination
                CAMX_ASSERT(0 == pOutputPortNegotiationData->numInputPortsNotification);
                CAMX_ASSERT(1 == pOutputPort->numInputPortsConnected);

                pOutputPortNegotiationData->numInputPortsNotification++;

                if (pOutputPortNegotiationData->numInputPortsNotification == pOutputPort->numInputPortsConnected)
                {
                    // When all the input ports connected to the output port have notified the output port, it means the
                    // output port has all the buffer requirements it needs to make a decision for the buffer on that output
                    // port
                    m_bufferNegotiationData.numOutputPortsNotified++;
                }

                m_outputPortsData.numSinkPorts++;
            }
            else
            {
                InitializeNonSinkPortBufferProperties(outputPortIndex, &pOutputPortCreateInfo->portLink);
            }
        }
    }

    if (CamxResultSuccess == result)
    {
        for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
        {
            const InputPortInfo* pInputPortInfo = &pNodeCreateInfo->inputPorts.portInfo[inputPortIndex];
            InputPort*           pInputPort     = &m_inputPortsData.pInputPorts[inputPortIndex];

            pInputPort->portSourceTypeId = pInputPortInfo->portSourceTypeId;

            if (TRUE == pInputPortInfo->flags.isSourceBuffer)
            {
                pCreateOutputData->createFlags.isSourceBuffer = TRUE;
                pInputPort->flags.isSourceBuffer              = TRUE;

                pInputPort->ppImageBuffers = static_cast<ImageBuffer**>(CAMX_CALLOC(sizeof(ImageBuffer*) *
                                                                                    MaxRequestQueueDepth));
                pInputPort->phFences       = static_cast<CSLFence*>(CAMX_CALLOC(sizeof(CSLFence) *
                                                                                MaxRequestQueueDepth));

                if ((NULL == pInputPort->ppImageBuffers) || (NULL == pInputPort->phFences))
                {
                    result = CamxResultENoMemory;
                    break;
                }
                else
                {
                    static const INT32      DeviceCount  = 1;
                    INT32                   deviceIndices[DeviceCount];
                    BufferProperties        bufferProperties;
                    BufferManagerCreateData createData   = { 0 };
                    createData.pBufferProperties         = &bufferProperties;
                    createData.pDeviceIndices            = &deviceIndices[0];
                    createData.deviceCount               = DeviceCount;
                    createData.maxBufferCount            = MaxRequestQueueDepth;
                    createData.immediateAllocBufferCount = MaxRequestQueueDepth;
                    createData.allocateBufferMemory      = FALSE;
                    createData.numBatchedFrames          = 1;

                    CHAR bufferManagerName[MaxStringLength64];
                    OsUtils::SNPrintF(bufferManagerName,
                        sizeof(bufferManagerName), "%s%dInputPort%d", Name(), InstanceID(), pInputPort->portId);
                    result = CreateImageBufferManager(bufferManagerName, &createData);
                    if (CamxResultSuccess != result)
                    {
                        CAMX_LOG_ERROR(CamxLogGroupCore, "Create ImageBufferManager failed");
                        break;
                    }

                    pInputPort->pImageBufferManager = createData.pImageBufferManager;
                    for (UINT bufferIndex = 0; bufferIndex < MaxRequestQueueDepth; bufferIndex++)
                    {
                        pInputPort->ppImageBuffers[bufferIndex] = createData.pImageBufferManager->GetImageBuffer();

                        if (NULL == pInputPort->ppImageBuffers[bufferIndex])
                        {
                            result = CamxResultENoMemory;
                            break;
                        }
                    }

                    if (CamxResultSuccess != result)
                    {
                        break;
                    }
                }
            }
        }

        if (CamxResultSuccess == result)
        {
            // Initialize the derived hw/sw node objects
            result = ProcessingNodeInitialize(pCreateInputData, pCreateOutputData);

            CAMX_ASSERT(MaxDependentFences >= pCreateOutputData->maxInputPorts);

            if (MaxDependentFences < pCreateOutputData->maxInputPorts)
            {
                CAMX_LOG_ERROR(CamxLogGroupCore, "ERROR: Need to increase the value of MaxDependentFences");

                result = CamxResultEFailed;
            }
        }

        if (CamxResultSuccess == result)
        {
            m_maxOutputPorts                         = pCreateOutputData->maxOutputPorts;
            m_maxInputPorts                          = pCreateOutputData->maxInputPorts;
            m_nodeFlags.isInplace                    = pCreateOutputData->createFlags.isInPlace;
            m_nodeFlags.callNotifyConfigDone         = pCreateOutputData->createFlags.willNotifyConfigDone;
            m_nodeFlags.canDRQPreemptOnStopRecording = pCreateOutputData->createFlags.canDRQPreemptOnStopRecording;

            /// @note Inplace nodes can only have one input and one output
            CAMX_ASSERT((FALSE == m_nodeFlags.isInplace) ||
                        ((1    == m_outputPortsData.numPorts) && (1 == m_inputPortsData.numPorts)));

            for (UINT request = 0; request < MaxRequestQueueDepth; request++)
            {
                if (0 != m_inputPortsData.numPorts)
                {
                    m_perRequestInfo[request].activePorts.pInputPorts =
                        static_cast<PerRequestInputPortInfo*>(CAMX_CALLOC(sizeof(PerRequestInputPortInfo) *
                                                                          m_inputPortsData.numPorts));

                    if (NULL == m_perRequestInfo[request].activePorts.pInputPorts)
                    {
                        result = CamxResultENoMemory;
                        break;
                    }
                }

                if (m_outputPortsData.numPorts > 0)
                {
                    m_perRequestInfo[request].activePorts.pOutputPorts =
                        static_cast<PerRequestOutputPortInfo*>(CAMX_CALLOC(sizeof(PerRequestOutputPortInfo) *
                                                                           m_outputPortsData.numPorts));

                    if (NULL == m_perRequestInfo[request].activePorts.pOutputPorts)
                    {
                        result = CamxResultENoMemory;
                        break;
                    }
                }
            }
        }
    }

    if (CamxResultSuccess == result)
    {
        CHAR  nodeMutexResource[Mutex::MaxResourceNameSize];

        OsUtils::SNPrintF(nodeMutexResource,
                          Mutex::MaxResourceNameSize,
                          "EPRLock_%s_%d",
                          m_pNodeName,
                          InstanceID());

        m_pProcessRequestLock = Mutex::Create(nodeMutexResource);
        m_pBufferReleaseLock  = Mutex::Create("BufferReleaseLock");
        if (NULL == m_pProcessRequestLock)
        {
            result = CamxResultENoMemory;
            CAMX_ASSERT("Node process request mutex creation failed");
        }

        if (NULL == m_pBufferReleaseLock)
        {
            result = CamxResultENoMemory;
            CAMX_ASSERT("Buffer release mutex creation failed");
        }

        m_pFenceCreateReleaseLock = Mutex::Create("Fence_Create_Release");
        if (NULL == m_pFenceCreateReleaseLock)
        {
            result = CamxResultENoMemory;
            CAMX_ASSERT("Node fence mutex creation failed");
        }

        OsUtils::SNPrintF(nodeMutexResource,
            Mutex::MaxResourceNameSize,
            "NodeBufferRequestLock_%s_%d",
            m_pNodeName,
            InstanceID());

        m_pBufferRequestLock = Mutex::Create(nodeMutexResource);
        if (NULL == m_pBufferRequestLock)
        {
            result = CamxResultENoMemory;
            CAMX_ASSERT("Node buffer request mutex creation failed");
        }
    }

    if (CamxResultSuccess == result)
    {
        if (TRUE == HwEnvironment::GetInstance()->GetStaticSettings()->watermarkImage)
        {
            /// @todo(CAMX-3165) Add IDs for hardware blocks inside camxhwdefs.h
            const UINT            IFE               = HwNodeIDStart;
            const UINT            IFEOutputPortFull = 0;
            const StaticSettings* pSettings         = HwEnvironment::GetInstance()->GetStaticSettings();

            switch (Type())
            {
                /// @todo (CAMX-2875) Need a good way to do comparisons with HWL node types and ports.
                case IFE: // IFE  Only watermark on IFE output
                    m_pWatermarkPattern = static_cast<WatermarkPattern*>(CAMX_CALLOC(sizeof(WatermarkPattern)));

                    if (NULL != m_pWatermarkPattern)
                    {
                        const CHAR* pOffset        = pSettings->watermarkOffset;
                        const UINT  length         = CAMX_ARRAY_SIZE(pSettings->watermarkOffset);
                        CHAR        offset[length] = { '\0' };
                        CHAR*       pContext       = NULL;
                        const CHAR* pXOffsetToken  = NULL;
                        const CHAR* pYOffsetToken  = NULL;

                        if (NULL != pOffset && 0 != pOffset[0])
                        {
                            Utils::Memcpy(offset, pOffset, length);
                            pXOffsetToken = OsUtils::StrTokReentrant(offset, "xX", &pContext);
                            pYOffsetToken = OsUtils::StrTokReentrant(NULL, "xX", &pContext);
                            if ((NULL != pXOffsetToken) && (NULL != pYOffsetToken))
                            {
                                m_pWatermarkPattern->watermarkOffsetX = OsUtils::StrToUL(pXOffsetToken, NULL, 0);
                                m_pWatermarkPattern->watermarkOffsetY = OsUtils::StrToUL(pYOffsetToken, NULL, 0);
                            }
                        }
                        result = ImageDump::InitializeWatermarkPattern(m_pWatermarkPattern);
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupCore, "Unable to allocate watermark structure. Out of memory");
                        result = CamxResultENoMemory;
                    }
                    break;
                default:
                    break;
            }
        }
    }

    CAMX_TRACE_SYNC_END(CamxLogGroupCore);
    CAMX_ASSERT(CamxResultSuccess == result);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::InitializeCmdBufferManagerList
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::InitializeCmdBufferManagerList(
    UINT maxNumCmdBufferManagers)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT((NULL == m_ppCmdBufferManagers) && (0 != maxNumCmdBufferManagers));

    m_maxNumCmdBufferManagers = 0;
    m_ppCmdBufferManagers     =
        static_cast<CmdBufferManager**>(CAMX_CALLOC(sizeof(CmdBufferManager*) * maxNumCmdBufferManagers));

    if (NULL == m_ppCmdBufferManagers)
    {
        CAMX_ASSERT_ALWAYS_MESSAGE("InitializeCmdBufferManagerList: CAMX_CALLOC failed");
        result = CamxResultENoMemory;
    }
    else
    {
        m_maxNumCmdBufferManagers = maxNumCmdBufferManagers;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::PrepareStreamOn
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::PrepareStreamOn()
{
    CamxResult result = OnStreamOn();

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::PrepareStreamOff
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::PrepareStreamOff(
    CHIDEACTIVATEPIPELINEMODE modeBitmask)
{
    CamxResult result = CamxResultSuccess;

    if (CamxResultSuccess == result)
    {
        result = OnStreamOff();
    }

    if ((CamxResultSuccess == result) &&
        (modeBitmask & CHIDeactivateModeReleaseBuffer))
    {
        result = ReleaseImageBuffers();
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::IsNodeDisabledWithOverride
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL Node::IsNodeDisabledWithOverride()
{
    return FALSE;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SetupRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::SetupRequest(
    PerBatchedFrameInfo* pPerBatchedFrameInfo,
    UINT*                pDifferentActiveStreams,
    UINT64               requestId,
    UINT64               syncId,
    BOOL*                pIsEnabled)
{
    CAMX_ASSERT(NULL != pIsEnabled);
    UINT                   requestIdIndex   = requestId % MaxRequestQueueDepth;

// struct NodePerRequestInfo
// {
//     PerRequestActivePorts activePorts;                  ///< Per request ports info passed to derived node
//     UINT                  numUnsignaledFences;          ///< Number of unsigned fences in a particular request. This is
//                                                         ///  decremented with each fence that triggers for the node. 0 means
//                                                         ///  all outputs are available.
//     UINT64                requestId;                    ///< incoming requestId
//     UINT                  metadataComplete;             ///< Binary flag representing whether metadata has been reported
//     UINT                  requestComplete;              ///< Binary flag representing whether request has been reported
// };

    PerRequestActivePorts* pRequestPorts    = &m_perRequestInfo[requestIdIndex].activePorts;

    m_tRequestId = requestId;

    CamxResult result = CamxResultSuccess;

    m_perRequestInfo[requestId % MaxRequestQueueDepth].numUnsignaledFences = 0;
    m_perRequestInfo[requestId % MaxRequestQueueDepth].requestId           = requestId;
    m_perRequestInfo[requestId % MaxRequestQueueDepth].metadataComplete    = 0;
    m_perRequestInfo[requestId % MaxRequestQueueDepth].requestComplete     = 0;

    // Set the CSL sync ID for this request ID
    SetCSLSyncId(requestId, syncId);

    if (NULL != pDifferentActiveStreams) // Has the active streams changed since the last request
    {
        NewActiveStreamsSetup(*pDifferentActiveStreams);
    }

    // The new set of streams for the current request may have disabled the node. For e.g. if this is a Node with
    // one sink port and that sink port is not active for the current request then the entire node is disabled
    if (TRUE == IsNodeEnabled())
    {
        result = SetupRequestOutputPorts(pPerBatchedFrameInfo);
        result = SetupRequestInputPorts(pPerBatchedFrameInfo);
        *pIsEnabled = TRUE;
    }
    else
    {
        UINT        tag     = GetNodeCompleteProperty();
        const UINT  one     = 1;
        const VOID* pOne[1] = { &one };
        WriteDataList(&tag, pOne, &one, 1);

        ProcessMetadataDone(m_tRequestId);
        ProcessRequestIdDone(m_tRequestId);
        *pIsEnabled = FALSE;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::ProcessRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::ProcessRequest(
    NodeProcessRequestData* pNodeRequestData,
    UINT64                  requestId)
{
    CAMX_ASSERT(pNodeRequestData != NULL);

    m_tRequestId = requestId;

    if (FALSE == m_parallelProcessRequests)
    {
        m_pProcessRequestLock->Lock();
    }

    BOOL       inErrorState   = m_pPipeline->RequestInErrorState(m_tRequestId);

    CamxResult result         = CamxResultEFailed;
    UINT       requestIdIndex = requestId % MaxRequestQueueDepth;

    pNodeRequestData->pCaptureRequest = m_pPipeline->GetRequest(requestId);

    CAMX_TRACE_SYNC_BEGIN_F(CamxLogGroupCore, "%s Node ProcessRequest %llu Sequence %u Pipeline %u",
                            NameAndInstanceId(), m_tRequestId, pNodeRequestData->processSequenceId, GetPipelineId());
    CAMX_LOG_DRQ("%s:%d Node ProcessRequest %llu on pipeline %d", m_pNodeName, InstanceID(), m_tRequestId, GetPipelineId());

    // Initially, set dependency pipeline id to own one
    for (UINT32 i = 0; i < MaxDependencies; i++)
    {
        for (UINT32 j = 0; j < MaxProperties; j++)
        {
            pNodeRequestData->dependencyInfo[i].propertyDependency.pipelineIds[j] = m_pPipeline->GetPipelineId();
        }
    }

    PerRequestActivePorts*    pRequestPorts      = &m_perRequestInfo[requestIdIndex].activePorts;
    ExecuteProcessRequestData executeProcessData = { 0 };
    executeProcessData.pNodeProcessRequestData   = pNodeRequestData;
    executeProcessData.pEnabledPortsInfo         = pRequestPorts;
    result = FillTuningModeData(reinterpret_cast<VOID**>(&executeProcessData.pTuningModeData));

    if (0 == executeProcessData.pNodeProcessRequestData->processSequenceId)
    {
        // Set buffer dependency for ports that has bypassable parent Node
        SetBufferDependency(&executeProcessData);
    }
    else if (0 != executeProcessData.pNodeProcessRequestData->processSequenceId)
    {
        // Update image buffer info for the port waiting on buffer dependency
        UpdateBufferInfoforPendingInputPorts(pRequestPorts);
    }

    if ((CamxResultSuccess == result) && (FALSE == inErrorState))
    {
        result = ExecuteProcessRequest(&executeProcessData);
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Node in error state.  Bypassing ExecuteProcessRequest");
        // This assumes while there is still a EPR to occur, the following two have not occurred
        ProcessMetadataDone(m_tRequestId);
        ProcessRequestIdDone(m_tRequestId);

        // Signal all fences immediately. Assume unsubmitted, but even if submitted, the check for isFenceSignalled will cover
        for (UINT portIndex = 0; portIndex < m_outputPortsData.numPorts; portIndex++)
        {
            if (TRUE == IsOutputPortEnabled(portIndex))
            {
                OutputPort* pOutputPort     = &m_outputPortsData.pOutputPorts[portIndex];
                UINT        maxImageBuffers = pOutputPort->bufferProperties.maxImageBuffers;

                if (maxImageBuffers > 0)
                {
                    NodeFenceHandlerData* pFenceHandlerData = &pOutputPort->pFenceHandlerData[m_tRequestId % maxImageBuffers];

                    pFenceHandlerData->fenceResult = CSLFenceResultFailed;
                    ProcessFenceCallback(pFenceHandlerData);
                }
            }
        }
    }

    if (CamxResultSuccess == result)
    {
        // Check if node is registered for config done, send config done notification if no dependencies
        if ((TRUE == m_nodeFlags.callNotifyConfigDone) &&
            (0 == executeProcessData.pNodeProcessRequestData->numDependencyLists))
        {
            m_pPipeline->NotifyNodeConfigDone(requestId);
        }
    }

    if (CamxResultSuccess != result)
    {
        for (UINT i = 0; i < pRequestPorts->numOutputPorts; i++)
        {
            if (NULL != pRequestPorts->pOutputPorts[i].phFence)
            {
                CAMX_ASSERT(CSLInvalidHandle != *(pRequestPorts->pOutputPorts[i].phFence));

                /// @note CSLFenceAsyncCancel expects we provide the handler and data that originally were registered;
                ///       however, currently, IFH only supports one handler/data per fence, and ignores the
                ///       handler/data values when canceling. If IFH changes to support more than one handler/data per
                ///       fence, then we need to pass those in here.

                /// @todo (CAMX-1738) Provide the callback data passed in on wait site here to avoid failing this.
                CSLFenceSignal(*(pRequestPorts->pOutputPorts[i].phFence), CSLFenceResultFailed);
            }
        }
    }

    if (FALSE == m_derivedNodeHandlesMetaDone)
    {
        BOOL hasDependency = FALSE;
        for (UINT index = 0; index < executeProcessData.pNodeProcessRequestData->numDependencyLists; index++)
        {
            DependencyUnit* pDependencyInfo = &executeProcessData.pNodeProcessRequestData->dependencyInfo[index];

            if (TRUE == HasAnyDependency(pDependencyInfo))
            {
                hasDependency = TRUE;
                break;
            }
        }

        if (FALSE == hasDependency)
        {
            ProcessMetadataDone(m_tRequestId);
        }
    }

    if ((0 == pNodeRequestData->numDependencyLists) || (CamxResultSuccess != result))
    {
        UINT        tag     = GetNodeCompleteProperty();
        const UINT  one     = 1;
        const VOID* pOne[1] = { &one };
        WriteDataList(&tag, pOne, &one, 1);
    }

    // For offline/reprocess requests, as the fence for a source buffer node is external we don't have the callback
    // We need to do either of the following -
    // 1) Release the input ImageBuffer when all the output buffer fences are signaled for the source buffer node,
    //    but that may be deemed too defensive, OR
    // 2) Use a pool (large enough) for container ImageBuffers for offline requests and recycle the LRU one

    // Going for (1) now, but need to be discussed further

    CAMX_TRACE_SYNC_END(CamxLogGroupCore);
    CAMX_LOG_DRQ("%s Node Complete ProcessRequest %llu on pipeline %d", m_pNodeName, m_tRequestId, GetPipelineId());

    if (FALSE == m_parallelProcessRequests)
    {
        m_pProcessRequestLock->Unlock();
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::RecycleRetiredCmdBuffers
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::RecycleRetiredCmdBuffers(
    UINT64 requestId)
{
    for (UINT i = 0; i < m_numCmdBufferManagers; i++)
    {
        CAMX_ASSERT(NULL != m_ppCmdBufferManagers[i]);

        /// @todo (CAMX-664) We recycle one requestId at a time when we could potentially do something like
        ///                  recycle all up to a certain requestId or recycle everything between
        ///                  requestId n - requestId n+x
        m_ppCmdBufferManagers[i]->RecycleAll(GetCSLSyncId(requestId));
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SetupRequestInputPorts
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::SetupRequestInputPorts(
    PerBatchedFrameInfo* pPerBatchedFrameInfo)
{
    CamxResult             result         = CamxResultSuccess;
    UINT                   requestIdIndex = m_tRequestId % MaxRequestQueueDepth;
    PerRequestActivePorts* pRequestPorts  = &m_perRequestInfo[requestIdIndex].activePorts;

    pRequestPorts->numInputPorts  = 0;

    for (UINT portIndex = 0; portIndex < m_inputPortsData.numPorts; portIndex++)
    {
        if (TRUE == IsInputPortEnabled(portIndex))
        {
            InputPort*               pInputPort            = &m_inputPortsData.pInputPorts[portIndex];
            PerRequestInputPortInfo* pPerRequestInputPort  = &pRequestPorts->pInputPorts[pRequestPorts->numInputPorts];
            OutputPortRequestedData  parentOutputPort      = { 0 };

            if (FALSE == IsSourceBufferInputPort(portIndex))
            {
                Node* pParentNode           = pInputPort->pParentNode;
                UINT  parentOutputPortIndex = pInputPort->parentOutputPortIndex;

                /// @todo (CAMX-954) Input ports of this node need to fetch all-batched-HAL-output-buffers
                ///                  from the parent in GetOutputPortInfo(..)

                if (m_tRequestId > pInputPort->bufferDelta)
                {
                    result = pParentNode->GetOutputPortInfo(
                        m_tRequestId - pInputPort->bufferDelta,
                        static_cast<UINT32>(pPerBatchedFrameInfo[0].sequenceId - pInputPort->bufferDelta),
                        parentOutputPortIndex,
                        &parentOutputPort);
                }

                if (CamxResultSuccess == result)
                {
                    // If the parent node is sensor it will return a NULL imagebuffer
                    if (NULL != parentOutputPort.pImageBuffer)
                    {
                        pPerRequestInputPort->portId           = pInputPort->portId;
                        if (TRUE == pParentNode->IsBypassableNode())
                        {
                            pPerRequestInputPort->pImageBuffer             = parentOutputPort.pImageBuffer;
                            pPerRequestInputPort->phFence                  = parentOutputPort.phFence;
                            pPerRequestInputPort->pIsFenceSignaled         = parentOutputPort.pIsFenceSignaled;
                            pPerRequestInputPort->pDelayedOutputBufferData = parentOutputPort.pDelayedOutputBufferData;
                            pPerRequestInputPort->flags.isPendingBuffer    = TRUE;
                            CAMX_LOG_DRQ(" %s Bypassable parent node, buffer selection is pending for port =%d",
                                m_pNodeName, pPerRequestInputPort->portId);
                        }
                        else
                        {
                            pPerRequestInputPort->pImageBuffer     = parentOutputPort.pImageBuffer;
                            pPerRequestInputPort->phFence          = parentOutputPort.phFence;
                            pPerRequestInputPort->pIsFenceSignaled = parentOutputPort.pIsFenceSignaled;
                        }

                        pRequestPorts->numInputPorts++;
                    }
                }
                else
                {
                    CAMX_ASSERT_ALWAYS_MESSAGE("Something wrong with node's input port!");

                    break;
                }
            }
            else
            {
                CaptureRequest*  pCaptureRequest = m_pPipeline->GetRequest(m_tRequestId);
                ChiStreamBuffer* pInputBuffer    = NULL;

                CAMX_ASSERT(1 == pCaptureRequest->numBatchedFrames);

                BOOL matchFound  = FALSE;
                UINT bufferId = 0;

                // Match the portId with the incoming request
                for (bufferId = 0; bufferId < pCaptureRequest->streamBuffers[0].numInputBuffers; bufferId++)
                {
                    if ((pInputPort->portId == pCaptureRequest->streamBuffers[0].inputBufferInfo[bufferId].portId) &&
                        (pCaptureRequest->streamBuffers[0].inputBufferInfo[bufferId].
                            inputBuffer.pStream->width == pInputPort->imageFormat.width) &&
                        (pCaptureRequest->streamBuffers[0].inputBufferInfo[bufferId].
                            inputBuffer.pStream->height == pInputPort->imageFormat.height))
                    {
                        pInputBuffer =
                            reinterpret_cast<ChiStreamBuffer*>(
                                &pCaptureRequest->streamBuffers[0].inputBufferInfo[bufferId].inputBuffer);
                        if (FALSE == IsRealTime())
                        {
                            CAMX_LOG_VERBOSE(CamxLogGroupCore,
                                " MFNR port %d, input buffer %p, node %d, instance %d, width %d, height %d",
                                pCaptureRequest->streamBuffers[0].inputBufferInfo[bufferId].portId,
                                pInputBuffer, Type(), InstanceID(), pInputPort->imageFormat.width,
                                pInputPort->imageFormat.height);
                        }
                        matchFound = TRUE;
                        break;
                    }
                }

                if (TRUE == matchFound)
                {
                    CAMX_ASSERT(NULL != pInputPort->ppImageBuffers);

                    ImageFormat* pImageFormat = &pInputPort->imageFormat;
                    ImageBuffer* pImageBuffer = pInputPort->ppImageBuffers[requestIdIndex];

                    CAMX_ASSERT(NULL != pImageBuffer);
                    CAMX_ASSERT(CSLInvalidHandle == pInputPort->phFences[requestIdIndex]);

                    pInputPort->phFences[requestIdIndex] =
                        pCaptureRequest->streamBuffers[0].inputBufferInfo[bufferId].fence;

                    BufferHandle* phNativeHandle = reinterpret_cast<BufferHandle*>(pInputBuffer->phBuffer);
                    UINT32        flags          = CSLMemFlagHw;

                    if (ChiExternalNode == Type())
                    {
                        if (TRUE == IsBypassableNode())
                        {
                            flags = (CSLMemFlagUMDAccess | CSLMemFlagHw);
                        }
                        else
                        {
                            flags = CSLMemFlagUMDAccess;
                        }
                    }

                    result = pImageBuffer->Import(pImageFormat,
                                                    *phNativeHandle,
                                                    0, // Offset
                                                    ImageFormatUtils::GetTotalSize(pImageFormat),
                                                    flags,
                                                    &m_deviceIndices[0],
                                                    m_deviceIndexCount);
                    if (result != CamxResultSuccess)
                    {
                        CAMX_LOG_ERROR(CamxLogGroupCore, "Import failed for Node %s m_instanceId %d",
                            m_pNodeName, m_instanceId);
                    }
                    pPerRequestInputPort->flags.isInputHALBuffer = TRUE;
                    pPerRequestInputPort->portId                 = pInputPort->portId;
                    pPerRequestInputPort->pImageBuffer           = pImageBuffer;
                    pPerRequestInputPort->phFence                = &pInputPort->phFences[requestIdIndex];
                    /// @todo (CAMX-1797) Handle input buffer fence signal status
                    pInputPort->isFenceSignaled                  = TRUE;
                    pPerRequestInputPort->pIsFenceSignaled       = &pInputPort->isFenceSignaled;

                    if (TRUE == HwEnvironment::GetInstance()->GetStaticSettings()->reprocessDump)
                    {

                        ImageDumpInfo dumpInfo = { 0 };

                        // Replace with real pipeline name as soon as it merges
                        CHAR pipelineName[256];
                        OsUtils::SNPrintF(pipelineName, sizeof(pipelineName), "%p", m_pPipeline);

                        dumpInfo.pPipelineName    = pipelineName;
                        dumpInfo.pNodeName        = m_pNodeName;
                        dumpInfo.nodeInstance     = m_instanceId;
                        dumpInfo.outputPort       = pInputPort->portId;
                        dumpInfo.requestId        = static_cast<UINT32>(m_tRequestId);
                        dumpInfo.batchId          = 0;
                        dumpInfo.numFramesInBatch = 0;
                        dumpInfo.pFormat          = pImageBuffer->GetFormat();
                        dumpInfo.width            = pInputPort->imageFormat.width;
                        dumpInfo.height           = pInputPort->imageFormat.height;
                        dumpInfo.pBaseAddr        = pImageBuffer->GetCPUAddress();

                        ImageDump::Dump(&dumpInfo);

                        // Dump meta data here pCaptureRequest->pMetadata
                    }
                    pRequestPorts->numInputPorts++;
                }
                else
                {
                    CAMX_LOG_WARN(CamxLogGroupCore, "No input buffers match node %d, port %d",
                        m_nodeType, pInputPort->portId);
                }
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SetupRequestOutputPorts
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::SetupRequestOutputPorts(
    PerBatchedFrameInfo* pPerBatchedFrameInfo)
{
    CamxResult             result         = CamxResultSuccess;
    UINT                   requestIdIndex = m_tRequestId % MaxRequestQueueDepth;

// struct NodePerRequestInfo
// {
//     PerRequestActivePorts activePorts;                  ///< Per request ports info passed to derived node
//     UINT                  numUnsignaledFences;          ///< Number of unsigned fences in a particular request. This is
//                                                         ///  decremented with each fence that triggers for the node. 0 means
//                                                         ///  all outputs are available.
//     UINT64                requestId;                    ///< incoming requestId
//     UINT                  metadataComplete;             ///< Binary flag representing whether metadata has been reported
//     UINT                  requestComplete;              ///< Binary flag representing whether request has been reported
// };

// struct PerRequestActivePorts
// {
//     UINT                      numOutputPorts;          ///< Number of output ports per request
//     UINT                      numInputPorts;           ///< Number of input ports per request
//     PerRequestOutputPortInfo* pOutputPorts;            ///< Per request output ports - points to array size numOutputPorts
//     PerRequestInputPortInfo*  pInputPorts;             ///< Per request input ports  - points to array size numInputPorts
// };
    PerRequestActivePorts* pRequestPorts  = &m_perRequestInfo[requestIdIndex].activePorts;

    pRequestPorts->numOutputPorts                        = 0;

// struct AllOutputPortsData
// {
//     UINT        numPorts;                       ///< Number of ports
//     OutputPort* pOutputPorts;                   ///< Pointer to an array of output ports
//     UINT        sinkPortIndices[MaxNumStreams]; ///< Array of output (SINK) port index (in the pOutputPorts array)
//     UINT        numSinkPorts;                   ///< Number of sink ports
// };
    // 遍历m_outputPortsData.pOutputPorts
    for (UINT portIndex = 0; portIndex < m_outputPortsData.numPorts; portIndex++)
    {
        if (TRUE == IsOutputPortEnabled(portIndex))
        {
            OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[portIndex];

            if (pOutputPort->bufferProperties.maxImageBuffers > 0)
            {
                // 提取属性
                UINT                      numBatchedFrames      = pOutputPort->numBatchedFrames;
                UINT                      maxImageBuffers       = pOutputPort->bufferProperties.maxImageBuffers;
                PerRequestOutputPortInfo* pRequestOutputPort    =
                    &m_perRequestInfo[requestIdIndex].activePorts.pOutputPorts[pRequestPorts->numOutputPorts];
                NodeFenceHandlerData*     pFenceHandlerData     =
                    &pOutputPort->pFenceHandlerData[(m_tRequestId % maxImageBuffers)];
                NodeFenceHandlerData*     pDelayedBufferFenceHandlerData =
                    &pOutputPort->pDelayedBufferFenceHandlerData[(m_tRequestId % maxImageBuffers)];

                pRequestOutputPort->portId           = pOutputPort->portId;
                pRequestOutputPort->numOutputBuffers = 0;

                pRequestOutputPort->flags.isBatch    = (numBatchedFrames > 1) ? TRUE : FALSE;
                pRequestOutputPort->flags.isLoopback = pOutputPort->flags.isLoopback;

                // Since nodes are walked by the topology in a random order, when some Node A requests its input port
                // information like input buffer/fences/etc from the parent node, and if the parent node is not called
                // by the topology yet, the parent node will create whatever is necessary to provide to the child node
                // that is calling it. Later when the parent node is called, it would have for e.g. create the fence for
                // this output port already
                /// @todo (CAMX-3119) remove IsTorchWidgetNode check below and handle this in generic way.
                if (((TRUE == IsSinkPortWithBuffer(portIndex)) || (TRUE == IsNonSinkHALBufferOutput(portIndex))) &&
                    (FALSE == IsTorchWidgetNode()))
                {
                    if ((TRUE == IsInplace()) && (numBatchedFrames > 1))
                    {
                        /// @todo (CAMX-527) Input ports of this node need to fetch all-batched-HAL-output-buffers
                        ///                  from the parent in GetOutputPortInfo(..)
                        CAMX_NOT_IMPLEMENTED();
                    }

                    UINT baseImageIndex         =
                        ((m_tRequestId * numBatchedFrames) % pOutputPort->bufferProperties.maxImageBuffers);
                    UINT     outputPortStreamId = pOutputPort->sinkTargetStreamId;
                    // typedef INT32 CSLFence;
                    CSLFence hOldFence          = pFenceHandlerData->hFence;
                    CSLFence hNewFence          = CSLInvalidHandle;

                    m_pFenceCreateReleaseLock->Lock();
                    // 释放hOldFence
                    if (CSLInvalidHandle != hOldFence)
                    {
                        result = CSLReleaseFence(hOldFence);

                        CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d), ImgBuf:%p reqId:%llu result:%d",
                            this, m_pNodeName, InstanceID(), &pFenceHandlerData->hFence, hOldFence,
                            pOutputPort->ppImageBuffers[0], m_tRequestId, result);

                        pFenceHandlerData->hFence = CSLInvalidHandle;
                    }

                    // TODO
                    result = CSLCreatePrivateFence("NodeOutputPortFence", &hNewFence);

                    CAMX_LOG_DRQ("CreatePrivateFence...Node: %08x, id: %s:%d, fence: %08x(%d), ImgBuf:%p reqId:%llu result:%d",
                        this, m_pNodeName, InstanceID(), &pFenceHandlerData->hFence, hNewFence, pOutputPort->ppImageBuffers[0],
                        m_tRequestId, result);
                    CAMX_ASSERT(CamxResultSuccess == result);

                    result = CSLFenceAsyncWait(hNewFence,
                                                Node::CSLFenceCallback,
                                                &pFenceHandlerData->nodeCSLFenceCallbackData);

                    CAMX_ASSERT(CamxResultSuccess == result);

                    // 更新pFenceHandlerData为newFence
                    pFenceHandlerData->requestId         = m_tRequestId;
                    pFenceHandlerData->hFence            = hNewFence;
                    pFenceHandlerData->isFenceSignaled   = FALSE;
                    pFenceHandlerData->numOutputBuffers  = 0;
                    m_pFenceCreateReleaseLock->Unlock();

                    // 注册pFenceHandlerData->hFence到pipeline，添加到链表中
                    m_pPipeline->RegisterRequestFence(&pFenceHandlerData->hFence, m_tRequestId);

                    CAMX_ASSERT(CSLInvalidHandle != hNewFence);

                    for (UINT i = 0; ((i < numBatchedFrames) && (CamxResultSuccess == result)); i++)
                    {
                        // Is the output port enabled for the frame in the batch
// struct PerBatchedFrameInfo
// {
//     UINT32          sequenceId;                             ///< Framework frame number
//     UINT            activeStreamIdMask;                     ///< Active Stream Id Mask
//     BufferHandle*   phBuffers[MaxNumStreams];               ///< Native Handles corresponding to activeStreamIds[index];
//                                                             ///  Buffers for the active streams indexed with streamId
// };
                        // 二进制位运算
                        if (TRUE == Utils::IsBitSet(pPerBatchedFrameInfo[i].activeStreamIdMask, outputPortStreamId))
                        {
                            UINT                    batchedFrameIndex       = (baseImageIndex + i);
                            FenceHandlerBufferInfo* pFenceHandlerBufferInfo =
                                &pFenceHandlerData->outputBufferInfo[pFenceHandlerData->numOutputBuffers];

                            ImageBuffer* pImageBuffer = NULL;
                            pImageBuffer = pOutputPort->ppImageBuffers[batchedFrameIndex];

                            CAMX_ASSERT(NULL == pImageBuffer);

                            if (NULL == pImageBuffer)
                            {
                                m_pBufferRequestLock->Lock();
                                pImageBuffer = pOutputPort->pImageBufferManager->GetImageBuffer();
                                m_pBufferRequestLock->Unlock();
                            }

                            if (NULL != pImageBuffer)
                            {
                                CAMX_ASSERT(FALSE == pImageBuffer->HasBackingBuffer());
                                CAMX_ASSERT(1     == m_deviceIndexCount); // HAL buffer is output by 1 device only

                                BufferHandle* phNativeHandle = pPerBatchedFrameInfo[i].phBuffers[outputPortStreamId];

                                CAMX_ASSERT(NULL != phNativeHandle);
                                if (NULL != phNativeHandle)
                                {
                                    const ImageFormat* pImageFormat = &pOutputPort->bufferProperties.imageFormat;

                                    UINT32 flags = CSLMemFlagHw;
                                    if (ChiExternalNode == Type())
                                    {
                                        // clear CSLMemFlagHW and set CSLMemFlagUMDAccess for ChiExternalNode
                                        flags ^= CSLMemFlagHw;
                                        flags |= CSLMemFlagUMDAccess;
                                    }
                                    // For non sink mode which need to map to HAL buffer, it need UMD access
                                    if (TRUE == IsNonSinkHALBufferOutput(portIndex))
                                    {
                                        flags |= CSLMemFlagUMDAccess;
                                    }
                                    /// @todo (CAMX-2106) Cannot blindly set UMD access, this breaks the ZSL implementation
                                    if (JPEGAggregator == Type())
                                    {
                                        flags = CSLMemFlagUMDAccess;
                                    }

                                    if (TRUE == IsSecureMode())
                                    {
                                        flags = (CSLMemFlagProtected | CSLMemFlagHw);
                                    }

                                    /// @todo (CAMX-1015) Calculate ImageFormatUtils::GetTotalSize only one and save it
                                    m_pBufferRequestLock->Lock();
                                    result = pImageBuffer->Import(pImageFormat,
                                                                  *phNativeHandle,
                                                                  0, // Offset
                                                                  ImageFormatUtils::GetTotalSize(pImageFormat),
                                                                  flags,
                                                                  &m_deviceIndices[0],
                                                                  m_deviceIndexCount);
                                    m_pBufferRequestLock->Unlock();

                                    /// @todo (CAMX-2106) If this assert fires it could be because of the UMDAccess flag and
                                    ///                   then the app will stop working
                                    CAMX_ASSERT(CamxResultSuccess == result);

                                    pOutputPort->ppImageBuffers[batchedFrameIndex] = pImageBuffer;
                                    pFenceHandlerBufferInfo->pImageBuffer          = pImageBuffer;
                                    pFenceHandlerBufferInfo->sequenceId            = pPerBatchedFrameInfo[i].sequenceId;
                                    pFenceHandlerBufferInfo->phNativeHandle        = phNativeHandle;

                                    // Sent to the derived node
                                    UINT numOutputBuffers = pRequestOutputPort->numOutputBuffers;

                                    pRequestOutputPort->pImageBuffer[numOutputBuffers] = pImageBuffer;
                                    pRequestOutputPort->phFence                        = &pFenceHandlerData->hFence;
                                    pRequestOutputPort->pIsFenceSignaled               =
                                        &pFenceHandlerData->isFenceSignaled;

                                    pRequestOutputPort->numOutputBuffers++;
                                    pFenceHandlerData->numOutputBuffers++;
                                }
                            }
                            else
                            {
                                CAMX_LOG_ERROR(CamxLogGroupCore, "No buffer was obtained from the ImageBufferManager");
                                result = CamxResultEInvalidPointer;
                            }
                        }
                    }

                    m_perRequestInfo[requestIdIndex].numUnsignaledFences++;
                    pRequestOutputPort->flags.isOutputHALBuffer = TRUE;
                }
                else if (FALSE == IsTorchWidgetNode()) // Output port that does not output HAL buffer
                {
                    // For ports outputting non-HAL buffers only the first entry is ever needed
                    UINT sequenceId = pPerBatchedFrameInfo[0].sequenceId;

                    pRequestOutputPort->flags.isOutputHALBuffer = FALSE;
                    pRequestOutputPort->numOutputBuffers        = 1;

                    result = ProcessNonSinkPortNewRequest(m_tRequestId, sequenceId, pOutputPort);

                    if (CamxResultSuccess == result)
                    {
                        // For ports outputting non-HAL buffers only the first entry is ever needed
                        // Data to be sent to the derived node that implements request processing
                        pRequestOutputPort->pImageBuffer[0]          = pFenceHandlerData->outputBufferInfo[0].pImageBuffer;
                        pRequestOutputPort->phFence                  = &pFenceHandlerData->hFence;
                        pRequestOutputPort->pIsFenceSignaled         = &pFenceHandlerData->isFenceSignaled;
                        pRequestOutputPort->phDelayedBufferFence     = &pDelayedBufferFenceHandlerData->hFence;
                        pRequestOutputPort->pDelayedOutputBufferData = &pFenceHandlerData->delayedOutputBufferData;
                        m_perRequestInfo[requestIdIndex].numUnsignaledFences++;
                        if (TRUE == IsBypassableNode())
                        {
                            pRequestOutputPort->flags.isDelayedBuffer = TRUE;
                            // For bypassable node, a fence for buffer dependency is added.
                            // So, this needs to be accounted here
                            m_perRequestInfo[requestIdIndex].numUnsignaledFences++;
                        }
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupCore,
                                       "No free output image buffers available for node %s port %d request %llu!!",
                                       m_pNodeName, pOutputPort->portId, m_tRequestId);
                    }
                }
            }

            pRequestPorts->numOutputPorts++;
        }
    }

    CAMX_ASSERT(pRequestPorts->numOutputPorts > 0);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ProcessEarlyMetadataDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::ProcessEarlyMetadataDone(
    UINT64 requestId)
{
    CAMX_TRACE_SYNC_BEGIN_F(CamxLogGroupCore, "%s:%d ProcessEarlyMetadataDone %llu", m_pNodeName, InstanceID(), requestId);

    // Early metadata doesn't care about all nodes being done. When this gets called it's gotten
    // everything it cares about and can skip the check that NotifyNodeMetadataDone normally does.
    m_pPipeline->ProcessMetadataRequestIdDone(requestId, TRUE);

    CAMX_TRACE_SYNC_END(CamxLogGroupCore);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ProcessMetadataDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::ProcessMetadataDone(
    UINT64 requestId)
{
    if (TRUE == CamxAtomicCompareExchangeU(&m_perRequestInfo[requestId % MaxRequestQueueDepth].metadataComplete, 0, 1))
    {
        CAMX_LOG_INFO(CamxLogGroupCore, "%s:%d ProcessMetadataDone %llu", m_pNodeName, InstanceID(), requestId);
        CAMX_TRACE_SYNC_BEGIN_F(CamxLogGroupCore, "%s:%d ProcessMetadataDone %llu", m_pNodeName, InstanceID(), requestId);

        m_pPipeline->NotifyNodeMetadataDone(requestId);

        CAMX_TRACE_SYNC_END(CamxLogGroupCore);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ProcessRequestIdDone
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::ProcessRequestIdDone(
    UINT64 requestId)
{
    if (TRUE == CamxAtomicCompareExchangeU(&m_perRequestInfo[requestId % MaxRequestQueueDepth].requestComplete, 0, 1))
    {
        CAMX_LOG_INFO(CamxLogGroupCore, "%s:%d ProcessRequestIdDone %llu", m_pNodeName, InstanceID(), requestId);
        CAMX_TRACE_SYNC_BEGIN_F(CamxLogGroupCore, "%s:%d ProcessRequestIdDone %llu", m_pNodeName, InstanceID(), requestId);

        RecycleRetiredCmdBuffers(requestId);

        /// @note It is assumed that if any output port is enabled, all the input ports are active i.e. all inputs
        ///       are required to generate any (and all) outputs
        for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
        {
            InputPort* pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];

            // Do not release reference if the buffer is needed for future request
            if (requestId > pInputPort->bufferDelta)
            {
                if (FALSE == IsSourceBufferInputPort(inputPortIndex))
                {
                    pInputPort->pParentNode->NotifyOutputConsumed(pInputPort->parentOutputPortIndex,
                                                                  requestId - pInputPort->bufferDelta);
                }
                else
                {
                    m_pFenceCreateReleaseLock->Lock();
                    UINT         requestIdIndex = (requestId - pInputPort->bufferDelta) % MaxRequestQueueDepth;
                    ImageBuffer* pImageBuffer   = pInputPort->ppImageBuffers[requestIdIndex];

                    CAMX_ASSERT(NULL != pImageBuffer);

                    // don't immediately release the hal buffer as it can be used by child node
                    // when node is bypassed
                    if ((TRUE == pImageBuffer->HasBackingBuffer()) && (FALSE == IsBypassableNode()))
                    {
                        pImageBuffer->Release();

                        CAMX_ASSERT(CSLInvalidHandle != pInputPort->phFences[requestIdIndex]);

                        CamxResult result = CSLReleaseFence(pInputPort->phFences[requestIdIndex]);

                        CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d), ImgBuf:%p reqId:%llu result:%d",
                            this, m_pNodeName, InstanceID(), &pInputPort->phFences[requestIdIndex],
                            pInputPort->phFences[requestIdIndex], pInputPort->ppImageBuffers[requestIdIndex], m_tRequestId,
                            result);

                        CAMX_ASSERT(result == CamxResultSuccess);

                        pInputPort->phFences[requestIdIndex] = CSLInvalidHandle;
                    }
                    m_pFenceCreateReleaseLock->Unlock();
                }
            }
        }

        CAMX_LOG_DRQ("Node Name: %s:%d, pipeline: %d, is done with request %llu",
            m_pNodeName, InstanceID(), GetPipelineId(), requestId);

        m_pPipeline->NotifyNodeRequestIdDone(requestId);

        CAMX_TRACE_SYNC_END(CamxLogGroupCore);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetDataCountFromPipeline
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SIZE_T Node::GetDataCountFromPipeline(
    const UINT   id,
    INT64        offset,
    UINT         pipelineID,
    BOOL         allowSticky)
{
    MetadataPool* pInputPool    = NULL;
    MetadataPool* pMainPool     = NULL;

    if (pipelineID == GetPipeline()->GetPipelineId())
    {
        pInputPool    = m_pInputPool;
        pMainPool     = m_pMainPool;
    }
    else
    {
        pInputPool    = GetIntraPipelinePerFramePool(PoolType::PerFrameInput, pipelineID);
        pMainPool     = GetIntraPipelinePerFramePool(PoolType::PerFrameResult, pipelineID);

        CAMX_ASSERT((NULL != pInputPool) && (NULL != pMainPool));
    }

    static const UINT InputProperty = ((static_cast<UINT32>(PropertyGroup::Result) << 16) | InputMetadataSectionMask);

    INT64         intReq = static_cast<INT64>(m_tRequestId);
    UINT64        index  = (intReq - offset <= 0) ? FirstValidRequestId : intReq - offset;
    MetadataSlot* pSlot  = NULL;
    SIZE_T        count  = 0;

    switch (id & DriverInternalGroupMask)
    {
        case 0:
            pSlot = pMainPool->GetSlot(index);
            pSlot->ReadLock();
            if (TRUE == pSlot->IsPublished(UnitType::Metadata, id))
            {
                count = pSlot->GetMetadataCountByTag(id, allowSticky);
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupCore, "Attempting to get metadata count for tag %08x when unpublished", id);
            }
            pSlot->Unlock();
            break;
        case InputMetadataSectionMask:
            // Input always published
            count = pInputPool->GetSlot(index)->GetMetadataCountByTag(id, allowSticky);
            break;
        case UsecaseMetadataSectionMask:
            CAMX_LOG_ERROR(CamxLogGroupCore, "Reading tag count for usecase pool tag %08x not supported", id);
            break;
        case StaticMetadataSectionMask:
            CAMX_LOG_ERROR(CamxLogGroupCore, "Reading tag count for static pool tag %08x not supported", id);
            break;
        case InputProperty:
        case static_cast<UINT32>(PropertyGroup::Result) << 16:
        case static_cast<UINT32>(PropertyGroup::Internal) << 16:
        case static_cast<UINT32>(PropertyGroup::Usecase) << 16:
        case static_cast<UINT32>(PropertyGroup::DebugData) << 16:
            CAMX_LOG_ERROR(CamxLogGroupCore, "Reading tag count for properties not supported. %08x", id);
            break;
        default:
            break;
    }

    return count;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetDataListFromPipeline
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetDataListFromPipeline(
    const UINT*  pDataList,
    VOID** const ppData,
    UINT64*      pOffsets,
    SIZE_T       length,
    BOOL*        pNegate,
    UINT         pipelineID)
{
    const StaticSettings* pSettings = HwEnvironment::GetInstance()->GetStaticSettings();

    MetadataPool* pInputPool    = NULL;
    MetadataPool* pMainPool     = NULL;
    MetadataPool* pInternalPool = NULL;
    MetadataPool* pUsecasePool  = NULL;

    if (pipelineID == GetPipeline()->GetPipelineId())
    {
        pInputPool    = m_pInputPool;
        pMainPool     = m_pMainPool;
        pInternalPool = m_pInternalPool;
        pUsecasePool  = m_pUsecasePool;
    }
    else
    {
        pInputPool    = GetIntraPipelinePerFramePool(PoolType::PerFrameInput, pipelineID);
        pMainPool     = GetIntraPipelinePerFramePool(PoolType::PerFrameResult, pipelineID);
        pInternalPool = GetIntraPipelinePerFramePool(PoolType::PerFrameInternal, pipelineID);
        pUsecasePool  = GetIntraPipelinePerFramePool(PoolType::PerUsecase, pipelineID);

        CAMX_ASSERT(NULL != pInputPool    &&
                    NULL != pMainPool     &&
                    NULL != pInternalPool &&
                    NULL != pUsecasePool);
    }

    static const UINT InputProperty = ((static_cast<UINT32>(PropertyGroup::Result) << 16) | InputMetadataSectionMask);

    for (SIZE_T i = 0; i < length; i++)
    {
        UINT32 group  = (pDataList[i] & DriverInternalGroupMask);
        VOID*  pBlob  = NULL;
        INT64  offset = ((NULL != pNegate) && (TRUE == pNegate[i])) ? (-1 * pOffsets[i]) : pOffsets[i];
        INT64  intReq = static_cast<INT64>(m_tRequestId);
        UINT64 index  = (intReq - offset <= 0) ? FirstValidRequestId : intReq - offset;

        if ((group != UsecaseMetadataSectionMask) &&
            (group != StaticMetadataSectionMask) &&
            (group != static_cast<UINT32>(PropertyGroup::Usecase) << 16))
        {
            // Unfortunate race condition that can lead to some properties coming from the requested request and
            // some from others In general, if a request is in Error state it doesnt matter what we give back,
            //  so really only providing a hopefully non-null pointer
            if (TRUE == m_pPipeline->RequestInErrorState(index))
            {
                UINT j;
                // adjust Get to point to a prior valid request
                for (j = 1; j < RequestQueueDepth; j++)
                {
                    if (FALSE == m_pPipeline->RequestInErrorState(index - j))
                    {
                        CAMX_LOG_ERROR(CamxLogGroupCore,
                                       "Requesting data from a request marked as an error."
                                       "Shifting from requestId: %lld to %lld",
                                       index, index - j);
                        index = (index - j);
                        break;
                    }
                }

                if (j == RequestQueueDepth)
                {
                    CAMX_LOG_ERROR(CamxLogGroupCore,
                                   "Couldn't find a valid request for data. This is really bad...using %lld", index);
                }
            }
        }

        MetadataSlot* pSlot = NULL;

        switch (group)
        {
            case 0:
                pSlot = pMainPool->GetSlot(index);
                if (NULL != pSlot)
                {
                    pSlot->ReadLock();
                    if (TRUE == pSlot->IsPublished(UnitType::Metadata, pDataList[i]))
                    {
                        pSlot->GetMetadataByTag(pDataList[i], &ppData[i]);
                    }
                    else
                    {
                        ppData[i] = NULL;
                    }
                    pSlot->Unlock();
                }
                break;
            case InputMetadataSectionMask:
                // Input always published
                pInputPool->GetSlot(index)->GetMetadataByTag(pDataList[i], &ppData[i]);
                break;
            case UsecaseMetadataSectionMask:
                pSlot = pUsecasePool->GetSlot(index);
                if (NULL != pSlot)
                {
                    pSlot->ReadLock();
                    if (TRUE == pSlot->IsPublished(UnitType::Metadata, pDataList[i]))
                    {
                        pSlot->GetMetadataByTag((pDataList[i] & ~UsecaseMetadataSectionMask), &ppData[i]);
                    }
                    else
                    {
                        ppData[i] = NULL;
                    }
                    pSlot->Unlock();
                }
                break;
            case StaticMetadataSectionMask:
                pSlot = m_pChiContext->GetStaticMetadataPool(GetPipeline()->GetCameraId())->GetSlot(index);
                if (NULL != pSlot)
                {
                    if (TRUE == pSlot->IsPublished(UnitType::Metadata, pDataList[i]))
                    {
                        pSlot->GetMetadataByTag(pDataList[i], &ppData[i]);
                    }
                    else
                    {
                        ppData[i] = NULL;
                    }
                }
                break;
            case InputProperty:
                pSlot = pInputPool->GetSlot(index);
                if (NULL != pSlot)
                {
                    pSlot->GetPropertyBlob(&pBlob);
                    // Input is always published, currently have no way to track if updated, so will always return valid pointer
                    CAMX_ASSERT(NULL != pBlob);
                    ppData[i] = Utils::VoidPtrInc(pBlob, MainPropertyOffsets[pDataList[i] & ~DriverInternalGroupMask]);
                }
                break;
            case static_cast<UINT32>(PropertyGroup::Result) << 16:
                CAMX_ASSERT(PropertyIDPerFrameResultEnd >= pDataList[i]);
                pSlot = pMainPool->GetSlot(index);
                if (NULL != pSlot)
                {
                    if (TRUE == pSlot->IsPublished(UnitType::Property, pDataList[i]))
                    {
                        pSlot->GetPropertyBlob(&pBlob);
                        CAMX_ASSERT(NULL != pBlob);
                        ppData[i] = (pBlob == NULL ) ? NULL :
                                    Utils::VoidPtrInc(pBlob, MainPropertyOffsets[pDataList[i] & ~DriverInternalGroupMask]);
                    }
                    else
                    {
                        ppData[i] = NULL;
                        CAMX_LOG_INFO(CamxLogGroupCore, "Property %08x %s Not Published for Request %llu",
                                      pDataList[i], HAL3MetadataUtil::GetPropertyName(pDataList[i]), index);
                    }
                }
                break;
            case static_cast<UINT32>(PropertyGroup::Internal) << 16:
                CAMX_ASSERT(PropertyIDPerFrameInternalEnd >= pDataList[i]);
                pSlot = pInternalPool->GetSlot(index);
                if (NULL != pSlot)
                {
                    if (TRUE == pSlot->IsPublished(UnitType::Property, pDataList[i]))
                    {
                        pSlot->GetPropertyBlob(&pBlob);
                        CAMX_ASSERT(NULL != pBlob);
                        ppData[i] = (pBlob == NULL ) ? NULL :
                                    Utils::VoidPtrInc(pBlob, InternalPropertyOffsets[pDataList[i] & ~DriverInternalGroupMask]);
                    }
                    else
                    {
                        ppData[i] = NULL;
                        CAMX_LOG_INFO(CamxLogGroupCore, "Property %08x %s Not Published for Request %llu",
                                      pDataList[i], HAL3MetadataUtil::GetPropertyName(pDataList[i]), index);
                    }
                }
                break;
            case static_cast<UINT32>(PropertyGroup::Usecase) << 16:
                CAMX_ASSERT(PropertyIDUsecaseEnd >= pDataList[i]);
                pSlot = pUsecasePool->GetSlot(index);
                if (NULL != pSlot)
                {
                    if (TRUE == pSlot->IsPublished(UnitType::Property, pDataList[i]))
                    {
                        pSlot->GetPropertyBlob(&pBlob);
                        CAMX_ASSERT(NULL != pBlob);
                        ppData[i] = (pBlob == NULL ) ? NULL :
                                    Utils::VoidPtrInc(pBlob, UsecasePropertyOffsets[pDataList[i] & ~DriverInternalGroupMask]);
                    }
                    else
                    {
                        ppData[i] = NULL;
                        CAMX_LOG_INFO(CamxLogGroupCore, "Property %08x %s Not Published for Request %llu",
                                      pDataList[i], HAL3MetadataUtil::GetPropertyName(pDataList[i]), index);
                    }
                }
                break;
            case static_cast<UINT32>(PropertyGroup::DebugData) << 16:
                CAMX_ASSERT(PropertyIDPerFrameDebugDataEnd >= pDataList[i]);
                pSlot = m_pDebugDataPool->GetSlot(index);
                if (NULL != pSlot)
                {
                    pSlot->GetPropertyBlob(&pBlob);
                    ppData[i] = Utils::VoidPtrInc(pBlob, DebugDataPropertyOffsets[pDataList[i] & ~DriverInternalGroupMask]);
                }
                break;
            default:
                break;
        }

        if (TRUE == pSettings->logMetaEnable)
        {
            if (NULL != pSlot)
            {
                CAMX_LOG_META("Node READ: %s, pipeline: %08x, request: %lld, offset: %d",
                    Name(), GetPipeline()->GetPipelineId(), intReq, offset);
                pSlot->PrintTagData(pDataList[i]);
            }
        }
    }

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetDataList
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetDataList(
    const UINT*  pDataList,
    VOID** const ppData,
    UINT64*      pOffsets,
    SIZE_T       length)
{
    return GetDataListFromPipeline(pDataList, ppData, pOffsets, length, NULL, GetPipeline()->GetPipelineId());
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetSensorModeRes0Data
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetSensorModeRes0Data(
    const SensorMode**    ppSensorData)
{
    CamxResult              result = CamxResultSuccess;
    MetadataSlot*           pCurrentSlot = NULL;
    UsecasePropertyBlob*    pUsecaseBlob = NULL;

    CAMX_ASSERT(NULL != ppSensorData);

    // There is only 1 usecase pool, so slot number will be always 0
    pCurrentSlot = m_pUsecasePool->GetSlot(0);
    CAMX_ASSERT(NULL != pCurrentSlot);
    CAMX_ASSERT(pCurrentSlot->IsPublished(UnitType::Property, PropertyIDUsecaseSensorModes));

    pCurrentSlot->ReadLock();
    pCurrentSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pUsecaseBlob));

    CAMX_ASSERT(NULL != pUsecaseBlob);

    *ppSensorData = &pUsecaseBlob->mUsecaseSensorModes.allModes[0];

    pCurrentSlot->Unlock();
    pCurrentSlot = NULL;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetSensorModeData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetSensorModeData(
    const SensorMode**    ppSensorData)
{
    CamxResult              result           = CamxResultSuccess;
    MetadataSlot*           pCurrentSlot     = NULL;
    UINT                    currentMode      = 0;
    UsecasePropertyBlob*    pUsecaseBlob     = NULL;
    static const UINT       GetProps[]       =
    {
        PropertyIDSensorCurrentMode,
    };
    static const UINT GetPropsLength          = CAMX_ARRAY_SIZE(GetProps);
    VOID*             pData[GetPropsLength]   = { 0 };
    UINT64            offsets[GetPropsLength] = { 0 };

    CAMX_ASSERT(NULL != ppSensorData);

    // There is only 1 usecase pool, so slot number will be always 0
    pCurrentSlot = m_pUsecasePool->GetSlot(0);
    CAMX_ASSERT(NULL != pCurrentSlot);
    CAMX_ASSERT(pCurrentSlot->IsPublished(UnitType::Property, PropertyIDUsecaseSensorModes));
    CAMX_ASSERT(pCurrentSlot->IsPublished(UnitType::Property, PropertyIDUsecaseSensorCurrentMode));

    pCurrentSlot->ReadLock();
    pCurrentSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pUsecaseBlob));

    CAMX_ASSERT(NULL != pUsecaseBlob);

    currentMode = pUsecaseBlob->mUsecaseSensorCurrentMode;
    // Initial Sensor mode can be non 0 as this can be set via CreateSession
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "initialSensorMode=%d", pUsecaseBlob->mUsecaseSensorCurrentMode);
    GetDataList(GetProps, pData, offsets, GetPropsLength);
    if (NULL != pData[0])
    {
        currentMode = *reinterpret_cast<UINT32*>(pData[0]);
    }
    CAMX_LOG_VERBOSE(CamxLogGroupISP, "currentSensorMode=%d", currentMode);
    *ppSensorData = &pUsecaseBlob->mUsecaseSensorModes.allModes[currentMode];

    pCurrentSlot->Unlock();
    pCurrentSlot = NULL;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetCameraConfiguration
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetCameraConfiguration(
    const CameraConfigurationInformation** ppCameraConfigInfo)
{
    CamxResult              result = CamxResultSuccess;
    MetadataSlot*           pCurrentSlot = NULL;
    UsecasePropertyBlob*    pUsecaseBlob = NULL;

    CAMX_ASSERT(NULL != ppCameraConfigInfo);

    // There is only 1 usecase pool, so slot number will be always 0
    pCurrentSlot = m_pUsecasePool->GetSlot(0);
    CAMX_ASSERT(NULL != pCurrentSlot);
    CAMX_ASSERT(pCurrentSlot->IsPublished(UnitType::Property, PropertyIDUsecaseCameraModuleInfo));

    pCurrentSlot->ReadLock();
    pCurrentSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pUsecaseBlob));

    CAMX_ASSERT(NULL != pUsecaseBlob);

    *ppCameraConfigInfo = &pUsecaseBlob->mUsecaseCameraModuleInfo;

    pCurrentSlot->Unlock();
    pCurrentSlot = NULL;

    return result;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::GetIFEInputResolution
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetIFEInputResolution(
    const IFEInputResolution** ppIFEInput)
{
    CamxResult              result = CamxResultSuccess;
    MetadataSlot*           pCurrentSlot = NULL;
    UsecasePropertyBlob*    pUsecaseBlob = NULL;

    CAMX_ASSERT(NULL != ppIFEInput);

    if (CamxResultSuccess == result)
    {
        // There is only 1 usecase pool, so slot number will be always 0
        pCurrentSlot = m_pUsecasePool->GetSlot(0);
        CAMX_ASSERT(NULL != pCurrentSlot);
        CAMX_ASSERT(pCurrentSlot->IsPublished(UnitType::Property, PropertyIDUsecaseIFEInputResolution));

        CAMX_LOG_INFO(CamxLogGroupHWL, "Node name %s", Name());

        pCurrentSlot->ReadLock();
        pCurrentSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pUsecaseBlob));

        CAMX_ASSERT(NULL != pUsecaseBlob);

        *ppIFEInput = &pUsecaseBlob->mUsecaseIFEInputResolution;

        pCurrentSlot->Unlock();
        pCurrentSlot = NULL;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetPreviewDimension
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetPreviewDimension(
    CamxDimension* pPreviewDimension)
{
    CamxResult              result          = CamxResultSuccess;
    MetadataSlot*           pCurrentSlot    = NULL;
    UsecasePropertyBlob*    pUsecaseBlob    = NULL;
    CAMX_ASSERT(NULL != m_pPipeline);
    MetadataPool*           pPerUsecasePool = m_pPipeline->GetPerFramePool(PoolType::PerUsecase);

    CAMX_ASSERT(NULL != pPreviewDimension);

    if (CamxResultSuccess == result)
    {
        // There is only 1 usecase pool, so slot number will be always 0
        pCurrentSlot = pPerUsecasePool->GetSlot(0);
        CAMX_ASSERT(NULL != pCurrentSlot);
        CAMX_ASSERT(TRUE == (pCurrentSlot->IsPublished(UnitType::Property, PropertyIDUsecasePipelineOutputDimensions)));

        pCurrentSlot->ReadLock();
        pCurrentSlot->GetPropertyBlob(reinterpret_cast<VOID**>(&pUsecaseBlob));

        CAMX_ASSERT(NULL != pUsecaseBlob);

        for (UINT i = 0; i < pUsecaseBlob->mUsecasePipelineOutputDimensions.numberOutputs; i++)
        {
            if (ChiPipelineOutputPreview == pUsecaseBlob->mUsecasePipelineOutputDimensions.dimensions[i].outputType)
            {
                CHIBUFFERDIMENSION outputDimension =
                    pUsecaseBlob->mUsecasePipelineOutputDimensions.dimensions[i].outputDimension;

                pPreviewDimension->width           = outputDimension.width;
                pPreviewDimension->height          = outputDimension.height;
                CAMX_LOG_VERBOSE(CamxLogGroupCore, "Preview dimension w %d h %d, type %d",
                                 pPreviewDimension->width,
                                 pPreviewDimension->height,
                                 pUsecaseBlob->mUsecasePipelineOutputDimensions.dimensions[i].outputType);
                break;
            }
        }

        pCurrentSlot->Unlock();
        pCurrentSlot = NULL;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::WritePreviousData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::WritePreviousData(
    UINT32 dataId,
    UINT   size)
{
    VOID*  pData      = NULL;
    UINT64 offsets[1] = {1};

    GetDataList(&dataId, &pData, &offsets[0], 1);
    // NOWHINE CP036a: Casting pointer to constant
    WriteDataList(&dataId, const_cast<const VOID**>(&pData), &size, 1);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::WriteData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAMX_INLINE CamxResult Node::WriteData(
    UINT64      requestId,
    UINT32      dataId,
    SIZE_T      size,
    const VOID* pData)
{
    const StaticSettings* pSettings = HwEnvironment::GetInstance()->GetStaticSettings();

    MetadataSlot* pSlot = NULL;
    UINT32        group = (dataId & DriverInternalGroupMask);
    VOID*         pBlob = NULL;
    VOID*         pPoolData = NULL;

    switch (group)
    {
        case 0:
            // Special tags get to be sent back early
            EarlyReturnTag(requestId, dataId, pData, size);

            pSlot = m_pMainPool->GetSlot(requestId);
            pSlot->SetMetadataByTag(dataId, pData, size);
            pSlot->PublishMetadataList(&dataId, 1);
            break;
        case InputMetadataSectionMask:
            CAMX_ASSERT_ALWAYS(); // Cant write Input
            break;
        case UsecaseMetadataSectionMask:
            pSlot = m_pUsecasePool->GetSlot(requestId);
            pSlot->SetMetadataByTag((dataId & ~UsecaseMetadataSectionMask), pData, size);
            pSlot->PublishMetadataList(&dataId, 1);
            break;
        case StaticMetadataSectionMask:
            m_pChiContext->GetStaticMetadataPool(GetPipeline()->GetCameraId())
                         ->GetSlot(requestId)
                         ->SetMetadataByTag(dataId, pData, size);
            break;
        case static_cast<UINT32>(PropertyGroup::Result) << 16:
            CAMX_ASSERT(PropertyIDPerFrameResultEnd >= dataId);
            pSlot = m_pMainPool->GetSlot(requestId);
            pSlot->GetPropertyBlob(&pBlob);

            CAMX_ASSERT(NULL != pBlob);

            // Node complete properties don't have data to update, just publish
            if ((PropertyIDNodeComplete0 <= dataId) && ((PropertyIDNodeComplete0 + NodeCompleteCount) > dataId))
            {
                pSlot->PublishProperty(dataId);
            }
            else
            {
                pPoolData = Utils::VoidPtrInc(pBlob, MainPropertyOffsets[dataId & ~DriverInternalGroupMask]);
            }
            break;
        case static_cast<UINT32>(PropertyGroup::Internal) << 16:
            CAMX_ASSERT(PropertyIDPerFrameInternalEnd >= dataId);
            pSlot = m_pInternalPool->GetSlot(requestId);
            pSlot->GetPropertyBlob(&pBlob);

            CAMX_ASSERT(NULL != pBlob);

            pPoolData = Utils::VoidPtrInc(pBlob, InternalPropertyOffsets[dataId & ~DriverInternalGroupMask]);
            break;
        case static_cast<UINT32>(PropertyGroup::Usecase) << 16:
            CAMX_ASSERT(PropertyIDUsecaseEnd >= dataId);
            pSlot = m_pUsecasePool->GetSlot(requestId);
            pSlot->GetPropertyBlob(&pBlob);

            CAMX_ASSERT(NULL != pBlob);

            pPoolData = Utils::VoidPtrInc(pBlob, UsecasePropertyOffsets[dataId & ~DriverInternalGroupMask]);
            break;
        default:
            break;
    }

    if ((NULL != pPoolData) && (NULL != pData))
    {
        CAMX_ASSERT_MESSAGE(pData != NULL, "NULL pointer provided for data of tag %08x", dataId);

        pSlot->WriteLock();
        Utils::Memcpy(pPoolData, pData, size);
        pSlot->Unlock();
        pSlot->PublishProperty(dataId);
    }

    if (TRUE == pSettings->logMetaEnable)
    {
        if (NULL != pSlot)
        {
            CAMX_LOG_META("Node WRITE: %s, pipeline: %08x, request: %lld",
                Name(), GetPipeline()->GetPipelineId(), requestId);
            pSlot->PrintTagData(dataId);
        }
    }

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::GetPerFramePool
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MetadataPool* Node::GetPerFramePool(
    PoolType poolType)
{
    MetadataPool* pPool = NULL;

    switch (poolType)
    {
        case PoolType::PerFrameInput:
        case PoolType::PerFrameResult:
        case PoolType::PerFrameInternal:
        case PoolType::PerUsecase:
            pPool = m_pPipeline->GetPerFramePool(poolType);
            break;

        case PoolType::PerFrameDebugData:
            pPool = m_pDebugDataPool;
            break;

        default:
            CAMX_ASSERT_ALWAYS_MESSAGE("Invalid per frame pool");
            break;
    }

    return pPool;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::WriteDataList
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::WriteDataList(
    const UINT*  pDataList,
    const VOID** ppData,
    const UINT*  pDataSize,
    SIZE_T       length)
{
    for (SIZE_T i = 0; i < length; i++)
    {
        WriteData(m_tRequestId, pDataList[i], pDataSize[i], ppData[i]);
    }

    if (NULL != m_pDRQ)
    {
        // Majority of write occur for result pool, so performing no detection of result updates
        // Taking over dispatch for tags/props so it occurs for batches just once instead of once per update
        m_pDRQ->DispatchReadyNodes();
    }

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::OutputPortIndex
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT Node::OutputPortIndex(
    UINT portId)
{
    UINT portIndex = 0;
    UINT port      = 0;

    for (port = 0; port < m_outputPortsData.numPorts; port++)
    {
        if (m_outputPortsData.pOutputPorts[port].portId == portId)
        {
            portIndex = port;
            break;
        }
    }

    CAMX_ASSERT(port < m_outputPortsData.numPorts);

    return portIndex;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SetInputLink
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::SetInputLink(
    UINT  nodeInputLinkIndex,
    UINT  nodeInputPortId,
    Node* pParentNode,
    UINT  parentPortId)
{
    UINT parentOutputPortIndex = pParentNode->OutputPortIndex(parentPortId);

    m_inputPortsData.pInputPorts[nodeInputLinkIndex].portId                = nodeInputPortId;
    m_inputPortsData.pInputPorts[nodeInputLinkIndex].parentOutputPortIndex = parentOutputPortIndex;
    m_inputPortsData.pInputPorts[nodeInputLinkIndex].pParentNode           = pParentNode;

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SetUpLoopBackPorts
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::SetUpLoopBackPorts(
    UINT  inputPortIndex)
{
    InputPort*           pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];

    // If the node is the same as ports parent node, its a loopback port
    if (this == m_inputPortsData.pInputPorts[inputPortIndex].pParentNode)
    {
        pInputPort->flags.isLoopback = TRUE;
        pInputPort->bufferDelta = 1;

        OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[pInputPort->parentOutputPortIndex];
        OutputPortNegotiationData* pOutputPortNegotiationData =
            &m_bufferNegotiationData.pOutputPortNegotiationData[pInputPort->parentOutputPortIndex];
        pOutputPort->flags.isLoopback                         = TRUE;
        pOutputPort->enabledInStreamMask                      = ((1 << MaxNumStreams) - 1);

        /// @note If an output port has a loopback destination (input port), it can be its only destination
        CAMX_ASSERT(0 == pOutputPortNegotiationData->numInputPortsNotification);
        CAMX_ASSERT(1 == pOutputPort->numInputPortsConnected);

        pOutputPortNegotiationData->numInputPortsNotification++;

        if (pOutputPortNegotiationData->numInputPortsNotification == pOutputPort->numInputPortsConnected)
        {
            // When all the input ports connected to the output port have notified the output port, it means the
            // output port has all the buffer requirements it needs to make a decision for the buffer on that output
            // port
            m_bufferNegotiationData.numOutputPortsNotified++;
        }
        m_bHasLoopBackPorts = TRUE;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SetupSourcePort
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::SetupSourcePort(
    UINT  nodeInputLinkIndex,
    UINT  nodeInputPortId)
{
    m_inputPortsData.pInputPorts[nodeInputLinkIndex].portId = nodeInputPortId;
    m_inputPortsData.pInputPorts[nodeInputLinkIndex].parentOutputPortIndex = 0;
    m_inputPortsData.pInputPorts[nodeInputLinkIndex].pParentNode = NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::DisableInputOutputLink
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::DisableInputOutputLink(
    UINT  nodeInputIndex)
{
    Node* pParentNode = m_inputPortsData.pInputPorts[nodeInputIndex].pParentNode;
    CAMX_LOG_INFO(CamxLogGroupHWL, "nodeInputIndex %d Parent Node name %s, parent o/p portID %d",
                   nodeInputIndex, pParentNode->Name(),
                   m_inputPortsData.pInputPorts[nodeInputIndex].parentOutputPortIndex);

    pParentNode->RemoveOutputDeviceIndices(m_inputPortsData.pInputPorts[nodeInputIndex].parentOutputPortIndex,
                                            m_deviceIndices, m_deviceIndexCount);
    // Disable the input port
    m_inputPortsData.pInputPorts[nodeInputIndex].portDisabled = TRUE;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetAllInputPortIds
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::GetAllInputPortIds(
    UINT* pNumPorts,
    UINT* pPortIds)
{
    CAMX_ASSERT(pNumPorts != NULL);
    CAMX_ASSERT(pPortIds  != NULL);

    if ((pNumPorts != NULL) && (pPortIds != NULL))
    {
        *pNumPorts = m_inputPortsData.numPorts;

        for (UINT i = 0; i < *pNumPorts; i++)
        {
            pPortIds[i] = m_inputPortsData.pInputPorts[i].portId;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetAllOutputPortIds
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::GetAllOutputPortIds(
    UINT* pNumPorts,
    UINT* pPortIds)
{
    CAMX_ASSERT(pNumPorts != NULL);
    CAMX_ASSERT(pPortIds  != NULL);

    if ((pNumPorts != NULL) && (pPortIds != NULL))
    {
        *pNumPorts = 0;
        for (UINT i = 0; i < m_outputPortsData.numPorts; i++)
        {
            if (m_outputPortsData.pOutputPorts[i].numInputPortsConnected >
                m_outputPortsData.pOutputPorts[i].numInputPortsDisabled)
            {
                pPortIds[*pNumPorts] = m_outputPortsData.pOutputPorts[i].portId;
                *pNumPorts = *pNumPorts + 1;
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::RemoveOutputDeviceIndices
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::RemoveOutputDeviceIndices(
    UINT         portIndex,
    const INT32* pDeviceIndices,
    UINT         deviceIndexCount)
{
    OutputPort* pPort = &(m_outputPortsData.pOutputPorts[portIndex]);
    INT32       newDeviceIndices[CamxMaxDeviceIndex];
    UINT        newDeviceIndexCount = 0;
    UINT        i;
    UINT        j;
    UINT        indicesRemoved = 0;

    for (i = 0; i < pPort->deviceCount; i++)
    {
        for (j = 0; j < deviceIndexCount; j++)
        {
            if (pDeviceIndices[j] == pPort->deviceIndices[i])
            {
                break;
            }
        }
        if (j == deviceIndexCount)
        {
            newDeviceIndices[newDeviceIndexCount++] = pPort->deviceIndices[i];
        }
        else
        {
            indicesRemoved++;
        }
    }
    if (0 < indicesRemoved)
    {
        for (i = 0; i < newDeviceIndexCount; i++)
        {
            pPort->deviceIndices[i] = newDeviceIndices[i];
        }
        pPort->deviceCount = newDeviceIndexCount;
        pPort->numInputPortsDisabled++;
    }
    else if (0 == deviceIndexCount)
    {
        // SWLs dont have a device index, and won't be bypassed, so the node with input would be only one 'device
        // so we can safely remove a reference
        pPort->numInputPortsDisabled++;
        pPort->deviceCount--;
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "No device indice found for removal");
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::IsPipelineStreamedOn
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL Node::IsPipelineStreamedOn()
{
    return m_pPipeline->IsStreamedOn();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ResetSecureMode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::ResetSecureMode(
    UINT32 portId)
{
    UINT  portIndex = OutputPortIndex(portId);

    m_outputPortsData.pOutputPorts[portIndex].flags.isSecurePort = FALSE;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::CSLFenceCallback
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::CSLFenceCallback(
    VOID*           pNodePrivateFenceData,
    CSLFence        hSyncFence,
    CSLFenceResult  fenceResult)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT(pNodePrivateFenceData != NULL);
    FenceCallbackData*    pFenceCallbackData    = static_cast<FenceCallbackData*>(pNodePrivateFenceData);
    Node*                 pNode                 = pFenceCallbackData->pNode;
    NodeFenceHandlerData* pNodeFenceHandlerData = NULL;

    if (NULL == pNode)
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Fence %d signaled on NULL node reference", hSyncFence);
        return;
    }
    pNodeFenceHandlerData = static_cast<NodeFenceHandlerData*>(pFenceCallbackData->pNodePrivateData);
    /// @todo (CAMX-493) Handle returned signal status code properly and deal with implications in the node processing and DRQ
    if (CSLFenceResultSuccess != fenceResult)
    {
        CAMX_LOG_ERROR(CamxLogGroupCore,
                       "Node:%d [%s] InstanceID:%d Fence %d signaled with failure %d in node fence handler FOR %llu",
                       pNode->Type(),
                       pNode->m_pNodeName,
                       pNode->InstanceID(),
                       hSyncFence,
                       fenceResult,
                       pNodeFenceHandlerData->requestId);
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupCore,
                      "Node:%d [%s] InstanceID:%d Fence %d signaled with success in node fence handler FOR %llu",
                      pNode->Type(),
                      pNode->m_pNodeName,
                      pNode->InstanceID(),
                      fenceResult,
                      pNodeFenceHandlerData->requestId);
    }
    CAMX_LOG_PERF_INFO(CamxLogGroupCore, "%s:%d CSLFenceCallback", pNode->m_pNodeName, pNode->InstanceID());

    CAMX_ASSERT(pNode != NULL);

    pNodeFenceHandlerData->fenceResult = fenceResult;
    VOID* pData[] = { pFenceCallbackData, NULL };

    ///@ todo (CAMX-3203) Verify fenceResult is handled in the job handlers
    if ((NULL             != pNode->GetThreadManager()) &&
        (InvalidJobHandle != pNode->GetJobFamilyHandle()))
    {
        result = pNode->GetThreadManager()->PostJob(pNode->GetJobFamilyHandle(), NULL, &pData[0], FALSE, FALSE);
        if (CamxResultSuccess == result)
        {
            CAMX_LOG_INFO(CamxLogGroupCore, "Node:%d InstanceID:%d Fence %d signaled with success in node fence handler",
                pNode->Type(), pNode->InstanceID(), hSyncFence);
        }
        else
        {
            CAMX_LOG_INFO(CamxLogGroupCore, "Node:%d InstanceID:%d Fence %d handler failed in node fence handler",
                pNode->Type(), pNode->InstanceID(), hSyncFence);
        }
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupCore, "Node:%d InstanceID:%d Fence %d signaled with invalid or retired fence handlers",
            pNode->Type(), pNode->InstanceID(), hSyncFence);
    }

    CAMX_ASSERT(CamxResultSuccess == result);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::NodeThreadJobFamilyCb
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID* Node::NodeThreadJobFamilyCb(
    VOID* pCbData)
{
    CAMX_ASSERT(NULL != pCbData);

    FenceCallbackData* pFenceCallbackData = static_cast<FenceCallbackData*>(pCbData);

    CAMX_ASSERT(pFenceCallbackData->pNode != NULL);

    NodeFenceHandlerData* pNodeFenceHandlerData = static_cast<NodeFenceHandlerData*>(pFenceCallbackData->pNodePrivateData);

    pFenceCallbackData->pNode->ProcessFenceCallback(pNodeFenceHandlerData);

    return NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ProcessFenceCallback
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::ProcessFenceCallback(
    NodeFenceHandlerData* pFenceHandlerData)
{
    CAMX_ASSERT(NULL != pFenceHandlerData);

    OutputPort* pOutputPort    = pFenceHandlerData->pOutputPort; // Output port to which the fence belongs to
    UINT64      requestId      = pFenceHandlerData->requestId;
    UINT        requestIdIndex = requestId % MaxRequestQueueDepth;

    CAMX_TRACE_SYNC_BEGIN_F(CamxLogGroupCore, "%s ProcessFenceCallback RequestId %llu Port %u",
                            m_nodeNameInstanceId, requestId, pOutputPort->portId);

    // Only do processing if we havent already signalled the fence (for failure cases)
    if (TRUE == CamxAtomicCompareExchangeU(&pFenceHandlerData->isFenceSignaled, 0, 1))
    {
        m_pPipeline->RemoveRequestFence(&pFenceHandlerData->hFence, requestId);

        ProcessingNodeFenceCallback(requestId, pOutputPort->portId);

        m_perRequestInfo[requestIdIndex].numUnsignaledFences--;

        NotifyRequestProcessingError(pFenceHandlerData);

        UINT numBatchedFrames = pOutputPort->numBatchedFrames;
        UINT baseImageIndex   = ((requestId * numBatchedFrames) % pOutputPort->bufferProperties.maxImageBuffers);

        WatermarkImage(pFenceHandlerData);
        DumpData(pFenceHandlerData);

        m_pBufferRequestLock->Lock();

        for (UINT i = 0; i < numBatchedFrames; i++)
        {
            if (NULL != pFenceHandlerData->outputBufferInfo[i].pImageBuffer)
            {
                UINT count = pFenceHandlerData->outputBufferInfo[i].pImageBuffer->ReleaseBufferManagerImageReference();

                if ((0 == count) && (FALSE == pOutputPort->flags.isNonSinkHALBufferOutput))
                {
                    pOutputPort->ppImageBuffers[baseImageIndex + i] = NULL;
                }
            }
        }

        if (TRUE == pOutputPort->flags.isSinkBuffer)
        {
            for (UINT i = 0; i < numBatchedFrames; i++)
            {
                CAMX_LOG_DRQ("Reporting sink fence callback for Fence (%d) node: %s:%d, pipeline: %d, seqId: %d, request: %llu",
                            static_cast<INT32>(pFenceHandlerData->hFence),
                            m_pNodeName,
                            InstanceID(),
                            GetPipelineId(),
                            pFenceHandlerData->outputBufferInfo[i].sequenceId,
                            pFenceHandlerData->requestId);

                CAMX_ASSERT(NULL != pFenceHandlerData->outputBufferInfo[i].pImageBuffer);
                CAMX_ASSERT(TRUE == pFenceHandlerData->outputBufferInfo[i].pImageBuffer->HasBackingBuffer());

                // HAL buffer can now be unmapped since the HW is done generating the output
                pFenceHandlerData->outputBufferInfo[i].pImageBuffer->Release();

                if (CSLFenceResultFailed == pFenceHandlerData->fenceResult)
                {
                    // Signal App that this request is an error
                    m_pPipeline->SinkPortFenceErrorSignaled(pOutputPort->sinkTargetStreamId,
                        pFenceHandlerData->outputBufferInfo[i].sequenceId,
                        pFenceHandlerData->requestId,
                        pFenceHandlerData->outputBufferInfo[i].phNativeHandle);
                }
                else
                {
                    m_pPipeline->SinkPortFenceSignaled(pOutputPort->sinkTargetStreamId,
                        pFenceHandlerData->outputBufferInfo[i].sequenceId,
                        pFenceHandlerData->requestId,
                        pFenceHandlerData->outputBufferInfo[i].phNativeHandle,
                        pFenceHandlerData->fenceResult);
                }

                CAMX_ASSERT(FALSE == pFenceHandlerData->outputBufferInfo[i].pImageBuffer->HasBackingBuffer());
            }
        }
        else
        {
            if (TRUE == pFenceHandlerData->isDelayedBufferFence)
            {
                CAMX_LOG_DRQ("Reporting delayed buffer fence callback for Fence (%d), node: %s:%d, pipeline: %d, "
                    "sequenceId: %d, request: %llu PortId: %d",
                    static_cast<INT32>(pFenceHandlerData->hFence),
                    m_pNodeName,
                    InstanceID(),
                    GetPipelineId(),
                    pFenceHandlerData->outputBufferInfo[0].sequenceId,
                    pFenceHandlerData->requestId,
                    pOutputPort->portId);
            }
            else
            {
                CAMX_LOG_DRQ("Reporting non-sink fence callback for Fence (%d), node: %s:%d, pipeline: %d, "
                    "sequenceId: %d, request: %llu PortId: %d",
                    static_cast<INT32>(pFenceHandlerData->hFence),
                    m_pNodeName,
                    InstanceID(),
                    GetPipelineId(),
                    pFenceHandlerData->outputBufferInfo[0].sequenceId,
                    pFenceHandlerData->requestId,
                    pOutputPort->portId);
            }

            if (CSLFenceResultFailed == pFenceHandlerData->fenceResult)
            {
                CAMX_LOG_ERROR(CamxLogGroupCore, "Fence error detected for Node: %s  requestId: %lld",
                    Name(), pFenceHandlerData->requestId);
                m_pPipeline->NonSinkPortFenceErrorSignaled(&pFenceHandlerData->hFence, pFenceHandlerData->requestId);
            }
            else
            {
                m_pPipeline->NonSinkPortFenceSignaled(&pFenceHandlerData->hFence, pFenceHandlerData->requestId);
            }
        }
        m_pBufferRequestLock->Unlock();

        if (0 == m_perRequestInfo[requestIdIndex].numUnsignaledFences)
        {
            ProcessMetadataDone(requestId);
            ProcessRequestIdDone(requestId);
        }
        else
        {
            CAMX_LOG_DRQ("Node %s:%d Pipeline:%d waiting for fenceCount %d for requestID: %llu",
                        m_pNodeName, InstanceID(), GetPipelineId(),
                        m_perRequestInfo[requestIdIndex].numUnsignaledFences, requestId);
        }

    }
    CAMX_TRACE_SYNC_END(CamxLogGroupCore);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::DumpData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::DumpData(
    NodeFenceHandlerData* pFenceHandlerData)
{
    const StaticSettings* pSettings        = HwEnvironment::GetInstance()->GetStaticSettings();
    OutputPort*           pOutputPort      = pFenceHandlerData->pOutputPort; // Output port to which the fence belongs to
    UINT                  numBatchedFrames = pOutputPort->numBatchedFrames;

    // Get the dump metadata
    CamxResult  result;
    UINT32      metaTag            = 0;
    UINT        debugTag[2]        = { 0 };
    VOID*       pData[2]           = { 0 };
    UINT        length             = CAMX_ARRAY_SIZE(debugTag);
    UINT64      debugDataOffset[2] = { 0 };

    result = VendorTagManager::QueryVendorTagLocation(
        "org.codeaurora.qcamera3.hal_private_data",
        "debug_image_dump",
        &metaTag);
    CAMX_ASSERT(CamxResultSuccess == result);

    debugTag[0] = (metaTag | InputMetadataSectionMask);
    result = VendorTagManager::QueryVendorTagLocation(
        "org.codeaurora.qcamera3.hal_private_data",
        "debug_image_dump_mask",
        &metaTag);
    debugTag[1] = (metaTag | InputMetadataSectionMask);
    CAMX_ASSERT(CamxResultSuccess == result);

    GetDataList(debugTag, pData, debugDataOffset, length);

    if ((TRUE == pSettings->autoImageDump)                                   ||
        ((TRUE == pSettings->dynamicImageDump) &&
         ((NULL != pData[0]) && (TRUE == *(static_cast<UINT8*>(pData[0]))))) ||
        ((TRUE == pSettings->dynamicImageDump) &&
         (TRUE == pSettings->dynamicImageDumpTrigger)))
    {
        CAMX_ASSERT((FALSE == pOutputPort->flags.isSinkBuffer) || (numBatchedFrames == 1));

        UINT32 enabledNodeMask = pSettings->autoImageDumpMask;
        BOOL   dump            = FALSE;

        // If we are dumping because the per-request dump VT is true, then use the mask in the request.
        if ((TRUE == pSettings->dynamicImageDump) &&
            ((NULL != pData[0]) && (TRUE == *(static_cast<UINT8*>(pData[0])))))
        {
            enabledNodeMask = *(static_cast<UINT8*>(pData[0]));
        }

        /// @todo (CAMX-2875) Need a good way to do comparisons with HWL node types.  Most of the things we have under
        ///                   the HWL aren't really HW specific anyway.
        switch (m_nodeType)
        {
            case 0x10000: // IFE
                dump = (FALSE == (enabledNodeMask & ImageDumpIFE))  ? FALSE : TRUE;
                break;
            case 0x10001: // JPEG
                dump = (FALSE == (enabledNodeMask & ImageDumpJPEG)) ? FALSE : TRUE;
                break;
            case 0x10002: // IPE
                dump = (FALSE == (enabledNodeMask & ImageDumpIPE))  ? FALSE : TRUE;
                break;
            case 0x10003: // BPS
                dump = (FALSE == (enabledNodeMask & ImageDumpBPS))  ? FALSE : TRUE;
                break;
            default:
                dump = (FALSE == (enabledNodeMask & ImageDumpMisc)) ? FALSE : TRUE;
                break;
        }

        if ((TRUE == dump) && (TRUE == pSettings->offlineImageDumpOnly))
        {
            if (TRUE == IsRealTime())
            {
                dump = FALSE;
            }
        }

        if (TRUE == dump)
        {
            for (UINT batchId = 0; batchId < numBatchedFrames; batchId++)
            {
                ImageDumpInfo dumpInfo = { 0 };

                // Replace with real pipeline name as soon as it merges
                CHAR pipelineName[256] = { 0 };
                OsUtils::SNPrintF(pipelineName, sizeof(pipelineName), "%p", m_pPipeline);

                dumpInfo.pPipelineName    = pipelineName;
                dumpInfo.pNodeName        = m_pNodeName;
                dumpInfo.nodeInstance     = m_instanceId;
                dumpInfo.outputPort       = pOutputPort->portId;
                dumpInfo.requestId        = static_cast<UINT32>(pFenceHandlerData->requestId);
                dumpInfo.batchId          = batchId;
                dumpInfo.numFramesInBatch = pFenceHandlerData->outputBufferInfo[batchId].pImageBuffer->GetNumFramesInBatch();
                dumpInfo.pFormat          = pFenceHandlerData->outputBufferInfo[batchId].pImageBuffer->GetFormat();
                dumpInfo.pBaseAddr        = pFenceHandlerData->outputBufferInfo[batchId].pImageBuffer->GetCPUAddress();
                dumpInfo.width            = pOutputPort->bufferProperties.imageFormat.width;
                dumpInfo.height           = pOutputPort->bufferProperties.imageFormat.height;

                ImageDump::Dump(&dumpInfo);
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::WatermarkImage
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::WatermarkImage(
    NodeFenceHandlerData* pFenceHandlerData)
{
    const StaticSettings* pSettings  = HwEnvironment::GetInstance()->GetStaticSettings();

    if (TRUE == pSettings->watermarkImage)
    {
        OutputPort*        pOutputPort       = pFenceHandlerData->pOutputPort; // Output port to which the fence belongs to
        UINT               numBatchedFrames  = pOutputPort->numBatchedFrames;
        UINT               portId            = pOutputPort->portId;
        const ImageFormat* pImageFormat      = pFenceHandlerData->outputBufferInfo[0].pImageBuffer->GetFormat();
        BOOL               watermark         = FALSE;
        /// @todo(CAMX-3165) Add IDs for hardware blocks inside camxhwdefs.h
        const UINT         IFE               = HwNodeIDStart;
        const UINT         IFEOutputPortFull = 0;

        CAMX_ASSERT((FALSE == pOutputPort->flags.isSinkBuffer) || (numBatchedFrames == 1));

        if (NULL == pImageFormat)
        {
            CAMX_LOG_ERROR(CamxLogGroupPProc, "Invalid pImageFormat");
            return;
        }

        switch (Type())
        {
            /// @todo (CAMX-2875) Need a good way to do comparisons with HWL node types and ports.
            case IFE: // IFE  Only watermark IFE full output for YUV format
                if ((IFEOutputPortFull == portId) && (Format::YUV420NV12 == pImageFormat->format))
                {
                    watermark = TRUE;
                }
                break;
            default:
                break;
        }

        if ((TRUE == watermark) && (NULL != m_pWatermarkPattern))
        {
            if (m_pWatermarkPattern->watermarkOffsetX > pImageFormat->width)
            {
                m_pWatermarkPattern->watermarkOffsetX = pImageFormat->width;
            }
            if (m_pWatermarkPattern->watermarkOffsetY > pImageFormat->height)
            {
                m_pWatermarkPattern->watermarkOffsetY = pImageFormat->height;
            }

            for (UINT i = 0; i < numBatchedFrames; i++)
            {
                ImageDumpInfo dumpInfo = { 0 };

                dumpInfo.requestId         = static_cast<UINT32>(pFenceHandlerData->requestId);
                dumpInfo.batchId           = i;
                dumpInfo.numFramesInBatch  = pFenceHandlerData->outputBufferInfo[i].pImageBuffer->GetNumFramesInBatch();
                dumpInfo.pFormat           = pFenceHandlerData->outputBufferInfo[i].pImageBuffer->GetFormat();
                dumpInfo.pBaseAddr         = pFenceHandlerData->outputBufferInfo[i].pImageBuffer->GetCPUAddress();
                dumpInfo.width             = pOutputPort->bufferProperties.imageFormat.width;
                dumpInfo.height            = pOutputPort->bufferProperties.imageFormat.height;
                dumpInfo.pWatermarkPattern = m_pWatermarkPattern;

                ImageDump::Watermark(&dumpInfo);
                pFenceHandlerData->outputBufferInfo[i].pImageBuffer->CacheOps(TRUE, TRUE);
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::CreateCmdBufferManager
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::CreateCmdBufferManager(
    const ResourceParams* pParams,
    CmdBufferManager**    ppCmdBufferManager)
{
    CAMX_ASSERT(m_numCmdBufferManagers < m_maxNumCmdBufferManagers);

    CamxResult result = CmdBufferManager::Create(pParams, ppCmdBufferManager);

    if (CamxResultSuccess == result)
    {
        CAMX_ASSERT(NULL != *ppCmdBufferManager);
        m_ppCmdBufferManagers[m_numCmdBufferManagers++] = *ppCmdBufferManager;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetCmdBufferForRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CmdBuffer* Node::GetCmdBufferForRequest(
    UINT64              requestId,
    CmdBufferManager*   pCmdBufferManager)
{
    CAMX_ASSERT(NULL != pCmdBufferManager);

    PacketResource* pPacketResource = NULL;

    if (CamxResultSuccess == pCmdBufferManager->GetBufferForRequest(GetCSLSyncId(requestId), &pPacketResource))
    {
        CAMX_ASSERT(TRUE == pPacketResource->GetUsageFlags().cmdBuffer);
    }

    // We know pPacketResource actually points to a CmdBuffer so we may static_cast
    return static_cast<CmdBuffer*>(pPacketResource);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::CheckCmdBufferWithRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CmdBuffer* Node::CheckCmdBufferWithRequest(
    UINT64              requestId,
    CmdBufferManager*   pCmdBufferManager)
{
    CAMX_ASSERT(NULL != pCmdBufferManager);

    PacketResource* pPacketResource = NULL;

    if (CamxResultSuccess == pCmdBufferManager->CheckBufferWithRequest(requestId, &pPacketResource))
    {
        CAMX_LOG_INFO(CamxLogGroupPProc, "checkbuffer");
        CAMX_ASSERT(TRUE == pPacketResource->GetUsageFlags().cmdBuffer);
    }

    // We know pPacketResource actually points to a CmdBuffer so we may static_cast
    return static_cast<CmdBuffer*>(pPacketResource);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetPacketForRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Packet* Node::GetPacketForRequest(
    UINT64              requestId,
    CmdBufferManager*   pCmdBufferManager)
{
    CAMX_ASSERT(NULL != pCmdBufferManager);

    PacketResource* pPacketResource = NULL;

    if (CamxResultSuccess == pCmdBufferManager->GetBufferForRequest(GetCSLSyncId(requestId), &pPacketResource))
    {
        CAMX_ASSERT(TRUE == pPacketResource->GetUsageFlags().packet);
    }

    // We know pPacketResource actually points to a Packet so we may static_cast
    return static_cast<Packet*>(pPacketResource);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetCmdBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CmdBuffer* Node::GetCmdBuffer(
    CmdBufferManager* pCmdBufferManager)
{
    PacketResource* pPacketResource = NULL;

    CAMX_ASSERT(NULL != pCmdBufferManager);

    if (CamxResultSuccess == pCmdBufferManager->GetBuffer(&pPacketResource))
    {
        CAMX_ASSERT(TRUE == pPacketResource->GetUsageFlags().cmdBuffer);
    }

    return static_cast<CmdBuffer*>(pPacketResource);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetPacket
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Packet* Node::GetPacket(
    CmdBufferManager* pCmdBufferManager)
{
    PacketResource* pPacketResource = NULL;

    CAMX_ASSERT(NULL != pCmdBufferManager);

    if (CamxResultSuccess == pCmdBufferManager->GetBuffer(&pPacketResource))
    {
        CAMX_ASSERT(TRUE == pPacketResource->GetUsageFlags().packet);
    }

    return static_cast<Packet*>(pPacketResource);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::AddOutputDeviceIndices
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::AddOutputDeviceIndices(
    UINT         portId,
    const INT32* pDeviceIndices,
    UINT         deviceIndexCount)
{
    UINT        portIndex = OutputPortIndex(portId);
    OutputPort* pPort     = &(m_outputPortsData.pOutputPorts[portIndex]);

    for (UINT i = 0; i < deviceIndexCount; i++)
    {
        BOOL alreadyInList = FALSE;

        for (UINT j = 0; j < pPort->deviceCount; j++)
        {
            if (pDeviceIndices[i] == pPort->deviceIndices[j])
            {
                alreadyInList = TRUE;
                break;
            }
        }

        if (FALSE == alreadyInList)
        {
            pPort->deviceIndices[pPort->deviceCount++] = pDeviceIndices[i];
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::AddDeviceIndex
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::AddDeviceIndex(
    INT32 deviceIndex)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT((m_deviceIndexCount + 1) <= CamxMaxDeviceIndex);

    if ((m_deviceIndexCount + 1) <= CamxMaxDeviceIndex)
    {
        m_deviceIndices[m_deviceIndexCount++] = deviceIndex;
        // Add the added device index to all output ports.
        for (UINT portIndex = 0; portIndex < m_outputPortsData.numPorts; portIndex++)
        {
            UINT portID = m_outputPortsData.pOutputPorts[portIndex].portId;
            AddOutputDeviceIndices(portID, &deviceIndex, 1);
        }
    }
    else
    {
        result = CamxResultEFailed;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::AddCSLDeviceHandle
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::AddCSLDeviceHandle(
    CSLDeviceHandle hCslDeiveHandle)
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT(m_cslDeviceCount < CamxMaxDeviceIndex);

    if (m_cslDeviceCount < CamxMaxDeviceIndex)
    {
        CAMX_LOG_VERBOSE(CamxLogGroupCore, "Added CSL Handle: %p at index: %d", hCslDeiveHandle, m_cslDeviceCount);
        m_hCSLDeviceHandles[m_cslDeviceCount++] = hCslDeiveHandle;
    }
    else
    {
        result = CamxResultEFailed;
    }

    return result;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::TriggerBufferNegotiation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::TriggerBufferNegotiation()
{
    CamxResult result = CamxResultSuccess;

    // The node can determine the input buffer requirements only after all its output ports have been notified of the buffer
    // requirements from all the input ports to which they are connected
    if (m_bufferNegotiationData.numOutputPortsNotified == m_outputPortsData.numPorts)
    {
        result = ProcessInputBufferRequirement();
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ProcessInputBufferRequirement
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::ProcessInputBufferRequirement()
{
    CamxResult result                          = CamxResultSuccess;
    BOOL       doBufferRequirementNotification = TRUE;

    CAMX_ASSERT(m_bufferNegotiationData.numOutputPortsNotified == m_outputPortsData.numPorts);

    if (FALSE == IsInplace())
    {
        result = ProcessingNodeFinalizeInputRequirement(&m_bufferNegotiationData);

        if ((CamxResultSuccess == result) && (Type() != StatsProcessing))
        {
            // Add the node to the list of nodes the topology needs to call for finalizing its buffer properties
            /// @todo (CAMX-1797) Need to find a better way to do the walk-forward path of buffer negotiation
            doBufferRequirementNotification = m_pPipeline->AddFinalizeBufferPropertiesNodeList(this);
        }
    }
    // If node was not added to the FinalizeBufferPropertiesNodeList then skip BufferRequirementNotification
    if ((CamxResultSuccess == result) && (TRUE == doBufferRequirementNotification))
    {
        ///@ todo (CAMX-346) Do we need to do anything special if there are multiple input ports?

        for (UINT input = 0; input < m_inputPortsData.numPorts; input++)
        {
            if (FALSE == IsSourceBufferInputPort(input))
            {
                Node*              pParentNode           = m_inputPortsData.pInputPorts[input].pParentNode;
                UINT               parentOutputPortIndex = m_inputPortsData.pInputPorts[input].parentOutputPortIndex;
                BufferRequirement* pBufferRequirement    = &m_bufferNegotiationData.inputBufferOptions[input].bufferRequirement;

                // If this assert fires check the FinalizeInputRequirement(..) function in the derived node
                if ((Type() != AutoFocus) && (Type() != StatsProcessing))
                {
                    CAMX_ASSERT(m_inputPortsData.pInputPorts[input].portId ==
                                m_bufferNegotiationData.inputBufferOptions[input].portId);
                }

                result = pParentNode->BufferRequirementNotification(parentOutputPortIndex, pBufferRequirement);

                if (CamxResultSuccess != result)
                {
                    break;
                }
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::BufferRequirementNotification
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::BufferRequirementNotification(
    UINT               outputPortIndex,
    BufferRequirement* pBufferRequirement)
{
    CamxResult                 result               = CamxResultSuccess;
    OutputPort*                pOutputPort          = &m_outputPortsData.pOutputPorts[outputPortIndex];
    OutputPortNegotiationData* pPortNegotiationData = &m_bufferNegotiationData.pOutputPortNegotiationData[outputPortIndex];

    pPortNegotiationData->inputPortRequirement[pPortNegotiationData->numInputPortsNotification] = *pBufferRequirement;

    pPortNegotiationData->numInputPortsNotification++;

    if (pPortNegotiationData->numInputPortsNotification == pOutputPort->numInputPortsConnected)
    {
        m_bufferNegotiationData.numOutputPortsNotified++;

        if (m_bufferNegotiationData.numOutputPortsNotified == m_outputPortsData.numPorts)
        {
            result = ProcessInputBufferRequirement();
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ProcessingNodeFinalizeInputRequirement
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::ProcessingNodeFinalizeInputRequirement(
    BufferNegotiationData* pBufferNegotiationData)
{
    CamxResult result = CamxResultSuccess;

    UINT32 numInputPorts = 0;
    UINT32 inputPortId[32];

    // Get Input Port List
    GetAllInputPortIds(&numInputPorts, &inputPortId[0]);

    pBufferNegotiationData->numInputPorts = numInputPorts;

    for (UINT input = 0; input < numInputPorts; input++)
    {
        pBufferNegotiationData->inputBufferOptions[input].nodeId     = Type();
        pBufferNegotiationData->inputBufferOptions[input].instanceId = InstanceID();
        pBufferNegotiationData->inputBufferOptions[input].portId     = inputPortId[input];

        BufferRequirement* pInputBufferRequirement = &pBufferNegotiationData->inputBufferOptions[input].bufferRequirement;

        pInputBufferRequirement->optimalWidth  = 0;
        pInputBufferRequirement->optimalHeight = 0;
        pInputBufferRequirement->minWidth      = 0;
        pInputBufferRequirement->maxWidth      = 0;
        pInputBufferRequirement->minHeight     = 0;
        pInputBufferRequirement->maxHeight     = 0;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::TriggerInplaceProcessing
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::TriggerInplaceProcessing()
{
    // If the node is an inplace node and it outputs an HAL buffer, it implies its parent node needs to be informed to
    // output a HAL buffer also
    /// @note Inplace nodes can only have one input and one output
    CAMX_ASSERT((TRUE == IsInplace()) && (TRUE == IsSinkPortWithBuffer(0)));

    NotifyParentHALBufferOutput();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::NotifyParentHALBufferOutput
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::NotifyParentHALBufferOutput()
{
    // Inplace node has only one input and one output
    UINT              parentOutputPortIndex = m_inputPortsData.pInputPorts[0].parentOutputPortIndex;
    OutputPort*       pOutputPort           = &m_outputPortsData.pOutputPorts[0];
    ChiStreamWrapper* pChiStreamWrapper     = m_pPipeline->GetOutputStreamWrapper(Type(), InstanceID(), pOutputPort->portId);


    /// @todo (CAMX-527) Need to pass the buffer properties, batch mode indicator etc to the parent node - just passing the
    ///                  streamId is not good enough. Basically we need to reorganize the code so that the parent also calls
    ///                  "InitializeSinkPortBufferProperties(" and it should have the same effect of a Node being created with
    ///                  prior knowledge that it outputs to a HAL buffer

    if (FALSE == IsSourceBufferInputPort(0))
    {
        m_inputPortsData.pInputPorts[0].pParentNode->NotifyHALBufferOutput(parentOutputPortIndex, pOutputPort,
            pChiStreamWrapper);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::TriggerOutputPortStreamIdSetup
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::TriggerOutputPortStreamIdSetup()
{
    CAMX_ASSERT(m_outputPortsData.numSinkPorts > 0); // This function shouldn't get called if it doesn't have sink port(s)

    for (UINT i = 0; i < m_outputPortsData.numSinkPorts; i++)
    {
        UINT        sinkPortIndex = m_outputPortsData.sinkPortIndices[i];
        OutputPort* pSinkPort = &m_outputPortsData.pOutputPorts[sinkPortIndex];
        UINT        sinkPortStreamIdMask = pSinkPort->enabledInStreamMask;

        /// @note It is assumed that if any output port is enabled, all the input ports are active i.e. all inputs are required
        ///       to generate any (and all) outputs
        for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
        {
            if (FALSE == IsSourceBufferInputPort(inputPortIndex))
            {
                InputPort* pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];

                if (this != pInputPort->pParentNode)
                {
                    pInputPort->pParentNode->SetOutputPortEnabledForStreams(pInputPort->parentOutputPortIndex,
                        sinkPortStreamIdMask);
                }
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::EnableParentOutputPorts
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::EnableParentOutputPorts()
{
    UINT streamId = ((1 << MaxNumStreams) - 1);
    for (UINT i = 0; i < m_outputPortsData.numPorts; i++)
    {
        OutputPort*         pOutputPort = &m_outputPortsData.pOutputPorts[i];
        if (pOutputPort->flags.isLoopback)
        {
            CAMX_LOG_VERBOSE(CamxLogGroupCore, "Loopback port id %d", pOutputPort->portId);
            for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
            {
                if (FALSE == IsSourceBufferInputPort(inputPortIndex))
                {
                    InputPort* pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];

                    if (this != pInputPort->pParentNode)
                    {
                        CAMX_LOG_VERBOSE(CamxLogGroupCore, "For input port %d Setting parent node %s port %d to stream %x",
                                      pInputPort->portId,
                                      pInputPort->pParentNode->m_pNodeName,
                                    pInputPort->pParentNode->m_outputPortsData.pOutputPorts[pInputPort->parentOutputPortIndex],
                                      streamId);
                        pInputPort->pParentNode->SetOutputPortEnabledForStreams(pInputPort->parentOutputPortIndex,
                                                                                    ((1 << MaxNumStreams) - 1));
                    }
                }
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SetOutputPortEnabledForStreams
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::SetOutputPortEnabledForStreams(
    UINT outputPortIndex,
    UINT streamIdMask)
{
    EnableOutputPortForStreams(outputPortIndex, streamIdMask);

    /// @note It is assumed that if any output port is enabled, all the input ports are active i.e. all inputs are required to
    ///       generate any (and all) outputs
    for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
    {
        if (FALSE == IsSourceBufferInputPort(inputPortIndex))
        {
            InputPort* pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];

            if (this != pInputPort->pParentNode)
            {
                pInputPort->pParentNode->SetOutputPortEnabledForStreams(pInputPort->parentOutputPortIndex, streamIdMask);
            }
            else
            {
                EnableOutputPortForStreams(pInputPort->parentOutputPortIndex, streamIdMask);
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::NewActiveStreamsSetup
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::NewActiveStreamsSetup(
    UINT activeStreamIdMask)
{
    m_nodeFlags.isEnabled = FALSE;

    UINT numOutputPortsEnabled = 0;

    for (UINT portIndex = 0; portIndex < m_outputPortsData.numPorts; portIndex++)
    {
// /// @brief Data for all output ports
// struct AllOutputPortsData
// {
//     UINT        numPorts;                       ///< Number of ports
//     OutputPort* pOutputPorts;                   ///< Pointer to an array of output ports
//     UINT        sinkPortIndices[MaxNumStreams]; ///< Array of output (SINK) port index (in the pOutputPorts array)
//     UINT        numSinkPorts;                   ///< Number of sink ports
// };

        OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[portIndex];

        // "enabledInStreamMask" is the list of streams for which the output port needs to be enabled. So if any stream in
        // "activeStreamIdMask" is also set in "enabledInStreamMask", it means we need to enable the output port
        if (0 != (pOutputPort->enabledInStreamMask & activeStreamIdMask) &&
            pOutputPort->numInputPortsConnected > pOutputPort->numInputPortsDisabled)
        {
            pOutputPort->flags.isEnabled = TRUE;
            numOutputPortsEnabled++;
        }
        else
        {
            pOutputPort->flags.isEnabled = FALSE;
        }
    }

    m_nodeFlags.isEnabled = ((0 == m_outputPortsData.numPorts) || (0 < numOutputPortsEnabled)) ? TRUE : FALSE;

    // Check for override to disable the node
    if (TRUE == IsNodeDisabledWithOverride())
    {
        m_nodeFlags.isEnabled = FALSE;
    }

    /// @note It is assumed that if any output port is enabled, all the input ports are active
    ///       i.e. all inputs are required to generate any (and all) outputs
    for (UINT portIndex = 0; portIndex < m_inputPortsData.numPorts; portIndex++)
    {
        InputPort* pInputPort       = &m_inputPortsData.pInputPorts[portIndex];
        if (FALSE == pInputPort->portDisabled)
        {
            pInputPort->flags.isEnabled = m_nodeFlags.isEnabled;
        }
        else
        {
            pInputPort->flags.isEnabled = FALSE;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetOutputPortInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetOutputPortInfo(
    UINT64                   requestId,
    UINT32                   sequenceId,
    UINT                     outputPortIndex,
    OutputPortRequestedData* pOutData)
{
    CamxResult  result      = CamxResultSuccess;
    OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[outputPortIndex];

    if (pOutputPort->bufferProperties.maxImageBuffers > 0)
    {
        result = ProcessNonSinkPortNewRequest(requestId, sequenceId, pOutputPort);

        if (CamxResultSuccess == result)
        {
            UINT                  imageFenceIndex   = requestId % pOutputPort->bufferProperties.maxImageBuffers;
            NodeFenceHandlerData* pFenceHandlerData = &pOutputPort->pFenceHandlerData[imageFenceIndex];
            NodeFenceHandlerData* pDelayedBufferFenceHandlerData =
                &pOutputPort->pDelayedBufferFenceHandlerData[imageFenceIndex];

            if (TRUE == IsBypassableNode() && ChiExternalNode == Type())
            {
                pOutData->phFence                  = &pDelayedBufferFenceHandlerData->hFence;
                pOutData->pIsFenceSignaled         = &pDelayedBufferFenceHandlerData->isFenceSignaled;
                pOutData->pImageBuffer             = reinterpret_cast<ImageBuffer*>(InvalidImageBuffer);
                pOutData->pDelayedOutputBufferData = &pFenceHandlerData->delayedOutputBufferData;

            }
            else
            {
                pOutData->phFence                  = &pFenceHandlerData->hFence;
                pOutData->pIsFenceSignaled         = &pFenceHandlerData->isFenceSignaled;
                pOutData->pImageBuffer             = pFenceHandlerData->outputBufferInfo[0].pImageBuffer;
                pOutData->pDelayedOutputBufferData = NULL;
            }
        }
    }

    CAMX_ASSERT(CamxResultSuccess == result);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ProcessNonSinkPortNewRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::ProcessNonSinkPortNewRequest(
    UINT64      requestId,
    UINT32      sequenceId,
    OutputPort* pOutputPort)
{
    CAMX_ASSERT((NULL != pOutputPort)                       &&
                (FALSE == pOutputPort->flags.isSinkBuffer)  &&
                (FALSE == pOutputPort->flags.isNonSinkHALBufferOutput));

    CamxResult result = CamxResultSuccess;

    if ((NULL != pOutputPort) && (pOutputPort->bufferProperties.maxImageBuffers > 0))
    {
        UINT                  imageFenceIndex   = requestId % pOutputPort->bufferProperties.maxImageBuffers;
        NodeFenceHandlerData* pFenceHandlerData = &pOutputPort->pFenceHandlerData[imageFenceIndex];
        NodeFenceHandlerData* pDelayedBufferFenceHandlerData =
            &pOutputPort->pDelayedBufferFenceHandlerData[imageFenceIndex];

        // Since nodes are walked by the topology in a random order, when some Node A requests output port information to some
        // other Node B, Node B may not have been called by the topology to process the request hence the node would not have
        // created the fence yet. If that is the case, create the fence here
        if (pFenceHandlerData->requestId != requestId)
        {
            m_pFenceCreateReleaseLock->Lock();
            CSLFence     hNewFence              = CSLInvalidHandle;
            CSLFence     hOldFence              = pFenceHandlerData->hFence;
            CSLFence     hNewDelayedBufferFence = CSLInvalidHandle;
            CSLFence     hOldDelayedBufferFence = pDelayedBufferFenceHandlerData->hFence;
            ImageBuffer* pNewImageBuffer        = NULL;

            // Reset previous entries
            pFenceHandlerData->hFence                    = CSLInvalidHandle;
            pDelayedBufferFenceHandlerData->hFence       = CSLInvalidHandle;
            pOutputPort->ppImageBuffers[imageFenceIndex] = NULL;

            if (CSLInvalidHandle != hOldFence)
            {
                result = CSLReleaseFence(hOldFence);

                CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d), reqId:%llu result: %d",
                    this, m_pNodeName, InstanceID(), &pFenceHandlerData->hFence, hOldFence,  m_tRequestId, result);

                pFenceHandlerData->hFence = CSLInvalidHandle;
                CAMX_ASSERT(result == CamxResultSuccess);
            }


            if (CSLInvalidHandle != hOldDelayedBufferFence)
            {
                result = CSLReleaseFence(hOldDelayedBufferFence);

                CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d), reqId:%llu result: %d",
                    this, m_pNodeName, InstanceID(), &pDelayedBufferFenceHandlerData->hFence, hOldDelayedBufferFence,
                    m_tRequestId, result);

                pDelayedBufferFenceHandlerData->hFence = CSLInvalidHandle;
                CAMX_ASSERT(result == CamxResultSuccess);
            }
            m_pFenceCreateReleaseLock->Unlock();

            if (FALSE == IsInplace())
            {
                // ImageBufferManager manages correct recycling of buffers
                pNewImageBuffer = pOutputPort->pImageBufferManager->GetImageBuffer();
            }
            else
            {
                // Inplace nodes can only ever have one input port. And its output image buffer is the same as its input buffer
                // so we get the parent nodes output buffer and make it this inplace nodes output buffer as well

                if (FALSE == IsSourceBufferInputPort(0))
                {
                    InputPort*              pInputPort            = &m_inputPortsData.pInputPorts[0];
                    Node*                   pParentNode           = pInputPort->pParentNode;
                    UINT                    parentOutputPortIndex = pInputPort->parentOutputPortIndex;
                    OutputPortRequestedData parentOutputPort      = { 0 };

                    pParentNode->GetOutputPortInfo(requestId, sequenceId, parentOutputPortIndex, &parentOutputPort);

                    pNewImageBuffer = parentOutputPort.pImageBuffer;
                }
            }

            if (NULL == pNewImageBuffer)
            {
                ///@ todo (CAMX-325) The requesting node should report a dependency back to Pipeline since it wasn't able
                ///                  to obtain a buffer
                result = CamxResultEResource;
                CAMX_ASSERT_ALWAYS_MESSAGE("pNewImageBuffer is NULL.");
                CAMX_LOG_ERROR(CamxLogGroupCore, "pNewImageBuffer is NULL as no buffer was obtained from ImageBufferManager");
            }

            m_pFenceCreateReleaseLock->Lock();
            if (CamxResultSuccess == result)
            {
                result = CSLCreatePrivateFence("NodeOutputPortFence", &hNewFence);

                CAMX_LOG_DRQ("CreatePrivateFence...Node: %08x, id: %s:%d, fence: %08x(%d), ImgBuf:%p reqId:%llu result: %d",
                    this, m_pNodeName, InstanceID(), &pFenceHandlerData->hFence, hNewFence,
                    pOutputPort->ppImageBuffers[imageFenceIndex], requestId, result);

                if ((CamxResultSuccess != result) || (CSLInvalidHandle == hNewFence))
                {
                    CAMX_ASSERT_ALWAYS_MESSAGE("Fence creation failed");
                    result = CamxResultEFailed;
                }
            }

            if (CamxResultSuccess == result)
            {
                result = CSLFenceAsyncWait(hNewFence, Node::CSLFenceCallback, &pFenceHandlerData->nodeCSLFenceCallbackData);

                if (CamxResultSuccess != result)
                {
                    CAMX_ASSERT_ALWAYS_MESSAGE("Fence wait failed");
                }
            }

            if (TRUE == IsBypassableNode())
            {
                if (CamxResultSuccess == result)
                {
                    result = CSLCreatePrivateFence("DelayedBufferFence", &hNewDelayedBufferFence);

                    CAMX_LOG_DRQ("CreatePrivateFence...Node: %08x, id: %s:%d, fence: %08x(%d), ImgBuf:%p reqId:%llu result: %d",
                        this, m_pNodeName, InstanceID(), &pFenceHandlerData->hFence, hNewFence,
                        pOutputPort->ppImageBuffers[imageFenceIndex], requestId, result);

                    if ((CamxResultSuccess != result) || (CSLInvalidHandle == hNewDelayedBufferFence))
                    {
                        CAMX_ASSERT_ALWAYS_MESSAGE("Fence creation failed for Delayed Buffer");
                        result = CamxResultEFailed;
                    }
                }

                if (CamxResultSuccess == result)
                {
                    result = CSLFenceAsyncWait(hNewDelayedBufferFence, Node::CSLFenceCallback,
                        &pDelayedBufferFenceHandlerData->nodeCSLFenceCallbackData);

                    if (CamxResultSuccess != result)
                    {
                        CAMX_ASSERT_ALWAYS_MESSAGE("Fence wait failed");
                    }
                }
            }
            m_pFenceCreateReleaseLock->Unlock();

            if (CamxResultSuccess != result)
            {
                if (NULL != pNewImageBuffer)
                {
                    /// @note GetBuffer() starts off with a refCnt of 1, so we decrement it by 1 in case of error
                    pNewImageBuffer->ReleaseBufferManagerImageReference();
                    pNewImageBuffer = NULL;
                }

                if (CSLInvalidHandle != hNewFence)
                {
                    m_pFenceCreateReleaseLock->Lock();
                    result = CSLReleaseFence(hNewFence);

                    CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d), reqID:%llu result: %d",
                        this, m_pNodeName, InstanceID(), &pFenceHandlerData->hFence, hNewFence, m_tRequestId, result);

                    CAMX_ASSERT(result == CamxResultSuccess);
                    pFenceHandlerData->hFence = CSLInvalidHandle;
                    m_pFenceCreateReleaseLock->Unlock();
                }

                if (CSLInvalidHandle != hNewDelayedBufferFence)
                {
                    m_pFenceCreateReleaseLock->Lock();
                    result = CSLReleaseFence(hNewDelayedBufferFence);

                    CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d), reqID:%llu result: %d",
                        this, m_pNodeName, InstanceID(), &pDelayedBufferFenceHandlerData->hFence, hNewDelayedBufferFence,
                        m_tRequestId, result);

                    CAMX_ASSERT(result == CamxResultSuccess);
                    pDelayedBufferFenceHandlerData->hFence = CSLInvalidHandle;
                    m_pFenceCreateReleaseLock->Unlock();
                }
            }
            else
            {
                CAMX_ASSERT(NULL != pNewImageBuffer);

                pFenceHandlerData->requestId              = requestId;
                pDelayedBufferFenceHandlerData->requestId = requestId;

                for (UINT i = 0; i < pOutputPort->numInputPortsConnected - pOutputPort->numInputPortsDisabled; i++)
                {
                    pNewImageBuffer->AddBufferManagerImageReference();
                }

                for (UINT i = 0; i < pOutputPort->numInputPortsConnectedinBypass; i++)
                {
                    pNewImageBuffer->AddBufferManagerImageReference();
                }

                CAMX_LOG_VERBOSE(CamxLogGroupCore, "Node: %08x, id: %s Num Ports connected %d Buffer %x Ref count %d",
                    this, m_pNodeName, pOutputPort->numInputPortsConnectedinBypass,
                    pNewImageBuffer,
                    pNewImageBuffer->GetReferenceCount());
            }

            m_pFenceCreateReleaseLock->Lock();
            /// @note This function is only called for non-sink ports that will have only one output buffer (even for HFR)
            pFenceHandlerData->hFence           = hNewFence;
            pFenceHandlerData->isFenceSignaled  = FALSE;
            pFenceHandlerData->numOutputBuffers = 1;
            pFenceHandlerData->outputBufferInfo[0].sequenceId            = sequenceId;
            pFenceHandlerData->outputBufferInfo[0].pImageBuffer          = pNewImageBuffer;
            pFenceHandlerData->delayedOutputBufferData.hFence            = CSLInvalidHandle;
            pFenceHandlerData->delayedOutputBufferData.pImageBuffer[0]   = NULL;
            pOutputPort->ppImageBuffers[imageFenceIndex] = pNewImageBuffer;

            m_pPipeline->RegisterRequestFence(&pFenceHandlerData->hFence, requestId);

            if ((TRUE == IsBypassableNode()) && (ChiExternalNode == Type()))
            {
                pDelayedBufferFenceHandlerData->hFence          = hNewDelayedBufferFence;
                pDelayedBufferFenceHandlerData->isFenceSignaled = FALSE;

                m_pPipeline->RegisterRequestFence(&pDelayedBufferFenceHandlerData->hFence, requestId);
            }
            else
            {
                pDelayedBufferFenceHandlerData->hFence = CSLInvalidHandle;
            }
            m_pFenceCreateReleaseLock->Unlock();
        }
    }

    CAMX_ASSERT(CamxResultSuccess == result);

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::OnStreamOn
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::OnStreamOn()
{
    CamxResult result = CamxResultSuccess;
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::OnStreamOff
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::OnStreamOff()
{
    CamxResult result = CamxResultSuccess;
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::NotifyPipelineCreated
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::NotifyPipelineCreated()
{
    CAMX_TRACE_SYNC_BEGIN_F(CamxLogGroupCore, "%s NotifyPipelineCreated", NameAndInstanceId());
    CamxResult result = CamxResultSuccess;

    result = PostPipelineCreate();

    CAMX_TRACE_SYNC_END(CamxLogGroupCore);
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::PostPipelineCreate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::PostPipelineCreate()
{
    CamxResult result = CamxResultSuccess;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::CreateBufferManagers
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::CreateBufferManagers()
{
    CamxResult result = CamxResultSuccess;

    if (Type() != Sensor)
    {
        for (UINT outputPortIndex = 0; outputPortIndex < m_outputPortsData.numPorts; outputPortIndex++)
        {
            OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[outputPortIndex];

            CAMX_ASSERT(pOutputPort->numInputPortsConnected >= pOutputPort->numInputPortsDisabled);
            // Unused port after buffer negotiation
            if (pOutputPort->numInputPortsConnected == pOutputPort->numInputPortsDisabled)
            {
                CAMX_FREE(pOutputPort->ppImageBuffers);
                CAMX_FREE(pOutputPort->pFenceHandlerData);
                CAMX_FREE(pOutputPort->pDelayedBufferFenceHandlerData);
                pOutputPort->ppImageBuffers                 = NULL;
                pOutputPort->pFenceHandlerData              = NULL;
                pOutputPort->pDelayedBufferFenceHandlerData = NULL;
                pOutputPort->bufferProperties.maxImageBuffers = 0;
            }

            if (pOutputPort->bufferProperties.maxImageBuffers > 0)
            {
                BOOL            isOutputHALBuffer = ((TRUE == IsSinkPort(outputPortIndex)) ||
                                                     (TRUE == IsNonSinkHALBufferOutput(outputPortIndex))) ? TRUE : FALSE;
                UINT            numBatchedFrames  = pOutputPort->numBatchedFrames;
                FormatParamInfo formatParamInfo   = {0};

                if (TRUE == IsSinkPort(outputPortIndex) ||
                    TRUE == IsNonSinkHALBufferOutput(outputPortIndex))
                {
                    ChiStreamWrapper* pChiStreamWrapper = NULL;
                    if (TRUE == IsNonSinkHALBufferOutput(outputPortIndex))
                    {
                        pChiStreamWrapper = pOutputPort->pChiStreamWrapper;
                    }
                    else
                    {
                        pChiStreamWrapper = m_pPipeline->GetOutputStreamWrapper(Type(),
                                                                                InstanceID(),
                                                                                pOutputPort->portId);
                    }

                    if (NULL != pChiStreamWrapper)
                    {
                        ChiStream* pChiStream           = reinterpret_cast<ChiStream*>(pChiStreamWrapper->GetNativeStream());

                        formatParamInfo.isHALBuffer     = isOutputHALBuffer;
                        formatParamInfo.isImplDefined   = ((pChiStream->format == ChiStreamFormatImplDefined) ||
                                                           (pChiStream->format == ChiStreamFormatYCrCb420_SP)) ? TRUE : FALSE;
                        formatParamInfo.grallocUsage    = pChiStream->grallocUsage;
                        if (TRUE == GetStaticSettings()->IsStrideSettingEnable)
                        {
                            formatParamInfo.planeStride = pChiStream->streamParams.planeStride;
                            formatParamInfo.sliceHeight = pChiStream->streamParams.sliceHeight;
                        }
                        else
                        {
                            formatParamInfo.planeStride = 0;
                            formatParamInfo.sliceHeight = 0;
                        }
                    }
                    else
                    {
                        result = CamxResultEFailed;
                    }
                    if (Format::Jpeg == pOutputPort->bufferProperties.imageFormat.format)
                    {
                        HwCameraInfo cameraInfo;
                        HwEnvironment::GetInstance()->GetCameraInfo(GetPipeline()->GetCameraId(), &cameraInfo);
                        formatParamInfo.maxJPEGSize = cameraInfo.pHwEnvironmentCaps->JPEGMaxSizeInBytes;
                        //  m_maxjpegsize is assigned when NodePropertyStitchMaxJpegSize property is defined for
                        //  Jpeg aggregator node in case of VR Dual Camera stiched snapshot
                        if (m_maxjpegsize != 0 && m_maxjpegsize > formatParamInfo.maxJPEGSize)
                        {
                            formatParamInfo.maxJPEGSize = m_maxjpegsize;
                        }
                    }
                }

                if ((Format::FDYUV420NV12 == pOutputPort->bufferProperties.imageFormat.format) ||
                    (Format::FDY8         == pOutputPort->bufferProperties.imageFormat.format))
                {
                    UINT32 baseFDResolution = static_cast<UINT32>(GetStaticSettings()->FDBaseResolution);

                    formatParamInfo.baseFDResolution.width  = baseFDResolution & 0xFFFF;
                    formatParamInfo.baseFDResolution.height = (baseFDResolution >> 16) & 0xFFFF;
                }

                if (CamxResultSuccess == result)
                {
                    ImageFormatUtils::InitializeFormatParams(&pOutputPort->bufferProperties.imageFormat,
                                                             &formatParamInfo);

                    BufferManagerCreateData createData   = { 0 };
                    createData.pBufferProperties         = &pOutputPort->bufferProperties;
                    createData.pDeviceIndices            = OutputPortDeviceIndices(outputPortIndex);
                    createData.deviceCount               = OutputPortDeviceCount(outputPortIndex);
                    createData.maxBufferCount            = GetMaximumNumOfBuffersToBeAllocated(
                                                            createData.pBufferProperties->maxImageBuffers);
                    createData.immediateAllocBufferCount = GetNumOfBuffersToBeAllocatedImmediately(
                                                            createData.pBufferProperties->immediateAllocImageBuffers,
                                                            createData.maxBufferCount);

                    if (TRUE == pOutputPort->flags.isSecurePort)
                    {
                        CAMX_ASSERT(TRUE == IsSecureMode());
                        createData.pBufferProperties->memFlags = (CSLMemFlagProtected | CSLMemFlagHw);
                    }
                    // Dont allocate buffer memory if it is a sinkport i.e. we just need a wrapper ImageBuffer with no backing
                    // memory allocation
                    createData.allocateBufferMemory    = ((TRUE == isOutputHALBuffer) ? FALSE : TRUE);
                    // Stats parse output buffers are fake
                    if (StatsParse == Type())
                    {
                        createData.allocateBufferMemory = FALSE;
                    }
                    // This is a multiplier for allocated bufferSize; Dont care for sinkports for which buffer memory is not
                    // allocated
                    createData.numBatchedFrames        = numBatchedFrames;

                    CAMX_LOG_VERBOSE(CamxLogGroupCore,
                        "[buf.camx] CreateImageBufferManager, node:%s, port:%d, allocate buffer:%d",
                        m_pNodeName, pOutputPort->portId, createData.allocateBufferMemory);

                    CHAR bufferManagerName[MaxStringLength64];
                    OsUtils::SNPrintF(bufferManagerName, sizeof(bufferManagerName), "%sPort%d",
                                      NameAndInstanceId(), pOutputPort->portId);
                    result = CreateImageBufferManager(bufferManagerName, &createData);

                    if (CamxResultSuccess == result)
                    {
                        pOutputPort->pImageBufferManager = createData.pImageBufferManager;
                    }
                    else
                    {
                        break;

                    }
                }
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::CreateImageBufferManager
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::CreateImageBufferManager(
    const CHAR*              pBufferManagerName,
    BufferManagerCreateData* pCreateData)
{
    CamxResult          result         = CamxResultSuccess;
    ImageBufferManager* pBufferManager = NULL;

    result = ImageBufferManager::Create(pBufferManagerName, &pBufferManager);

    if (CamxResultSuccess == result)
    {
        CAMX_ASSERT(NULL != pBufferManager);

        CAMX_LOG_INFO(CamxLogGroupCore, "BufferManagerName = %s, immediateAllocBufferCount = %d, maxBufferCount = %d",
                      pBufferManagerName, pCreateData->immediateAllocBufferCount, pCreateData->maxBufferCount);

        result = pBufferManager->InitializeBuffers(&pCreateData->pBufferProperties->imageFormat,
                                                   pCreateData->maxBufferCount,
                                                   pCreateData->immediateAllocBufferCount,
                                                   pCreateData->pBufferProperties->memFlags,
                                                   pCreateData->pDeviceIndices,
                                                   pCreateData->deviceCount,
                                                   pCreateData->allocateBufferMemory,
                                                   pCreateData->numBatchedFrames);

        if (CamxResultSuccess != result)
        {
            pBufferManager->Destroy();
            pBufferManager = NULL;
        }
    }

    if (CamxResultSuccess == result)
    {
        pCreateData->pImageBufferManager = pBufferManager;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::FillTuningModeData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::FillTuningModeData(
    VOID** ppTuningModeData)
{
    CamxResult    result  = CamxResultSuccess;
    UINT32        metaTag = 0;

    result = VendorTagManager::QueryVendorTagLocation("org.quic.camera2.tuning.mode",
                                                      "TuningMode",
                                                      &metaTag);

    if (CamxResultSuccess == result)
    {
        UINT   props[]                         = { metaTag | InputMetadataSectionMask };
        VOID*  pData[CAMX_ARRAY_SIZE(props)]   = {0};
        UINT64 offsets[CAMX_ARRAY_SIZE(props)] = {0};
        GetDataList(props, pData, offsets, CAMX_ARRAY_SIZE(props));

        *ppTuningModeData = pData[0];
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::DetermineBufferProperties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::DetermineBufferProperties()
{
    BOOL isAnyOutputPortNonSink = FALSE;

    for (UINT outputPortIndex = 0; outputPortIndex < m_outputPortsData.numPorts; outputPortIndex++)
    {
        if (FALSE == IsNonSinkHALBufferOutput(outputPortIndex))
        {
            isAnyOutputPortNonSink = TRUE;
            break;
        }
    }

    if (TRUE == isAnyOutputPortNonSink)
    {
        for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
        {
            InputPortNegotiationData* pInputPortNegotiationData =
                &m_bufferNegotiationData.pInputPortNegotiationData[inputPortIndex];

            InputPort* pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];

            if (FALSE == IsSourceBufferInputPort(inputPortIndex))
            {
                Node* pParentNode           = pInputPort->pParentNode;
                UINT  parentOutputPortIndex = pInputPort->parentOutputPortIndex;

                pInputPortNegotiationData->pImageFormat = pParentNode->GetOutputPortImageFormat(parentOutputPortIndex);
                pInputPortNegotiationData->inputPortId  = pInputPort->portId;

                CAMX_ASSERT(NULL != pInputPortNegotiationData->pImageFormat);
            }
            else
            {
                pInputPortNegotiationData->pImageFormat = &pInputPort->imageFormat;
                pInputPortNegotiationData->inputPortId  = pInputPort->portId;
            }
        }

        FinalizeBufferProperties(&m_bufferNegotiationData);
    }
}

// 使用传入管道中的数据填充节点
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::FinalizeInitialization
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::FinalizeInitialization(
    FinalizeInitializationData* pFinalizeInitializationData)
{
    CAMX_TRACE_SYNC_BEGIN_F(CamxLogGroupCore, "%s FinalizeInitialization", NameAndInstanceId());
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT(NULL != m_pPipeline);

    m_pInputPool     = m_pPipeline->GetPerFramePool(PoolType::PerFrameInput);
    m_pMainPool      = m_pPipeline->GetPerFramePool(PoolType::PerFrameResult);
    m_pEarlyMainPool = m_pPipeline->GetPerFramePool(PoolType::PerFrameResultEarly);
    m_pInternalPool  = m_pPipeline->GetPerFramePool(PoolType::PerFrameInternal);
    m_pUsecasePool   = m_pPipeline->GetPerFramePool(PoolType::PerUsecase);
    m_pDebugDataPool = pFinalizeInitializationData->pDebugDataPool;
    m_pThreadManager = pFinalizeInitializationData->pThreadManager;
    m_pDRQ           = pFinalizeInitializationData->pDeferredRequestQueue;
    m_hThreadJobFamilyHandle = pFinalizeInitializationData->hThreadJobFamilyHandle;

    CAMX_ASSERT(m_pInputPool != NULL);
    CAMX_ASSERT(m_pMainPool != NULL);
    CAMX_ASSERT(m_pEarlyMainPool != NULL);
    CAMX_ASSERT(m_pInternalPool != NULL);
    CAMX_ASSERT(m_pUsecasePool != NULL);
    CAMX_ASSERT(m_pDebugDataPool != NULL);

    CAMX_ASSERT(pFinalizeInitializationData->usecaseNumBatchedFrames > 0);

    if (Sensor != Type())
    {
        for (UINT outputPortIndex = 0; outputPortIndex < m_outputPortsData.numPorts; outputPortIndex++)
        {
            OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[outputPortIndex];

            if (TRUE == pOutputPort->flags.isBatchMode)
            {
                pOutputPort->numBatchedFrames = pFinalizeInitializationData->usecaseNumBatchedFrames;
            }
            else
            {
                // When the node is created it should be initialized to 1 as the default value
                CAMX_ASSERT((1 == pOutputPort->numBatchedFrames) || (pOutputPort->flags.isSinkNoBuffer == TRUE));
            }

            pOutputPort->bufferProperties.maxImageBuffers =
                (pOutputPort->bufferProperties.maxImageBuffers * pOutputPort->numBatchedFrames);

            if (pOutputPort->bufferProperties.maxImageBuffers > 0)
            {
                pOutputPort->ppImageBuffers = static_cast<ImageBuffer**>(
                    CAMX_CALLOC(sizeof(ImageBuffer*) * pOutputPort->bufferProperties.maxImageBuffers));

                pOutputPort->pFenceHandlerData = static_cast<NodeFenceHandlerData*>(
                    CAMX_CALLOC(sizeof(NodeFenceHandlerData) * pOutputPort->bufferProperties.maxImageBuffers));

                pOutputPort->pDelayedBufferFenceHandlerData = static_cast<NodeFenceHandlerData*>(
                    CAMX_CALLOC(sizeof(NodeFenceHandlerData) * pOutputPort->bufferProperties.maxImageBuffers));

                if ((NULL == pOutputPort->ppImageBuffers) || (NULL == pOutputPort->pFenceHandlerData) ||
                    (NULL == pOutputPort->pDelayedBufferFenceHandlerData))
                {
                    CAMX_ASSERT_ALWAYS_MESSAGE("Node::Initialize Cannot allocate memory!");
                    result = CamxResultENoMemory;
                    break;
                }
                else
                {
                    UINT maxImageBuffers = pOutputPort->bufferProperties.maxImageBuffers;

                    for (UINT imageFenceIndex = 0; imageFenceIndex < maxImageBuffers; imageFenceIndex++)
                    {
                        NodeFenceHandlerData* pFenceHandlerData = &pOutputPort->pFenceHandlerData[imageFenceIndex];
                        NodeFenceHandlerData* pDelayedBufferFenceHandlerData =
                            &pOutputPort->pDelayedBufferFenceHandlerData[imageFenceIndex];

                        // ExtFenceHandlerData is what we pass to CSL when we register a callback
                        // (the external main fence handler), CSL simply passes ExtFenceHandlerData
                        // back to us in the external handler
                        pFenceHandlerData->nodeCSLFenceCallbackData.pNode            = this;
                        pFenceHandlerData->nodeCSLFenceCallbackData.pNodePrivateData = pFenceHandlerData;
                        pFenceHandlerData->pOutputPort                               = pOutputPort;

                        pDelayedBufferFenceHandlerData->nodeCSLFenceCallbackData.pNode            = this;
                        pDelayedBufferFenceHandlerData->nodeCSLFenceCallbackData.pNodePrivateData =
                            pDelayedBufferFenceHandlerData;
                        pDelayedBufferFenceHandlerData->isDelayedBufferFence                      = TRUE;
                        pDelayedBufferFenceHandlerData->pOutputPort                               = pOutputPort;
                    }
                }
            }
            else
            {
                pOutputPort->ppImageBuffers                 = NULL;
                pOutputPort->pFenceHandlerData              = NULL;
                pOutputPort->pDelayedBufferFenceHandlerData = NULL;
            }
        }

        if (CamxResultSuccess == result)
        {
            for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
            {
                if (TRUE == IsSourceBufferInputPort(inputPortIndex))
                {
                    InputPort*        pInputPort        = &m_inputPortsData.pInputPorts[inputPortIndex];
                    UINT              inputPortId       = pInputPort->portId;
                    ChiStreamWrapper* pChiStreamWrapper = m_pPipeline->GetInputStreamWrapper(Type(), InstanceID(),
                                                              inputPortId);

                    if (NULL != pChiStreamWrapper)
                    {
                        pInputPort->sourceTargetStreamId = pChiStreamWrapper->GetStreamIndex();
                        InitializeSourcePortBufferFormat(inputPortIndex, pChiStreamWrapper);
                    }
                    else
                    {
                        CAMX_LOG_ERROR(CamxLogGroupCore, "pChiStreamWrapper for Port Index at %d is null", inputPortIndex);
                        result = CamxResultEInvalidPointer;
                        break;
                    }
                }
            }
        }
    }

    if (CamxResultSuccess == result)
    {
        result = ProcessingNodeFinalizeInitialization(pFinalizeInitializationData);
    }

    CAMX_TRACE_SYNC_END(CamxLogGroupCore);
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SwitchNodeOutputFormat
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::SwitchNodeOutputFormat(
    Format format)
{
    CamxResult result = CamxResultSuccess;

    for (UINT outputPortIndex = 0; outputPortIndex < m_outputPortsData.numPorts; outputPortIndex++)
    {
        /// @todo (CAMX-1797) Loop thru in the Chi override client
        /// Buffer requirements failed with current format, switch format to NV12 and try again. Ideally, we should loop thru
        /// all valid supported formats

        OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[outputPortIndex];

        if (TRUE == pOutputPort->flags.isSinkBuffer)
        {
            ChiStreamWrapper* pChiStreamWrapper =
                m_pPipeline->GetOutputStreamWrapper(Type(), InstanceID(), pOutputPort->portId);

            if (NULL != pChiStreamWrapper)
            {
                ChiStream*        pChiStream        = reinterpret_cast<ChiStream*>(pChiStreamWrapper->GetNativeStream());
                Format            selectedFormat    = pChiStreamWrapper->GetInternalFormat();

                if ((pChiStream->format == static_cast<CHISTREAMFORMAT>(HALPixelFormatImplDefined)) &&
                    (TRUE == ImageFormatUtils::IsUBWC(selectedFormat)))
                {
                    GrallocUsage grallocUsage = pChiStream->grallocUsage;
                    // Switching from UBWC, update the gralloc flags appropriately
                    grallocUsage &= ~(GrallocUsagePrivate0);
                    grallocUsage &= ~(GrallocUsagePrivate3);
                    pChiStreamWrapper->SetNativeGrallocUsage(grallocUsage);

                    CAMX_LOG_INFO(CamxLogGroupCore,
                                  "Unable to process with format %d, falling back to %d",
                                  pChiStreamWrapper->GetInternalFormat(),
                                  format);

                    pChiStreamWrapper->SetInternalFormat(format);

                    m_pChiContext->SetChiStreamInfo(pChiStreamWrapper, m_pPipeline->GetNumBatchedFrames());

                    OutputPortNegotiationData* pOutputPortNegotiationData =
                        &(m_bufferNegotiationData.pOutputPortNegotiationData[outputPortIndex]);

                    pOutputPortNegotiationData->pFinalOutputBufferProperties->imageFormat.format = format;
                }
            }
            else
            {
                CAMX_LOG_ERROR(CamxLogGroupCore, "pChiStreamWrapper for Port Index at %d is null", outputPortIndex);
                result = CamxResultEInvalidPointer;
                break;
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ResetBufferNegotiationData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::ResetBufferNegotiationData()
{
    CAMX_LOG_VERBOSE(CamxLogGroupCore, "node %d instance %d outputports %d ",
                     Type(),
                     InstanceID(),
                     m_bufferNegotiationData.numOutputPortsNotified);

    m_bufferNegotiationData.numOutputPortsNotified = 0;

    for (UINT outputPortIndex = 0; outputPortIndex < m_outputPortsData.numPorts; outputPortIndex++)
    {
        OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[outputPortIndex];

        OutputPortNegotiationData* pOutputPortNegotiationData =
            &m_bufferNegotiationData.pOutputPortNegotiationData[outputPortIndex];

        pOutputPortNegotiationData->numInputPortsNotification = 0;

        if ((TRUE == pOutputPort->flags.isSinkBuffer) || (TRUE == pOutputPort->flags.isSinkNoBuffer))
        {
            pOutputPortNegotiationData->numInputPortsNotification++;

            if (pOutputPortNegotiationData->numInputPortsNotification == pOutputPort->numInputPortsConnected)
            {
                m_bufferNegotiationData.numOutputPortsNotified++;
            }
        }
        else if (TRUE == pOutputPort->flags.isLoopback)
        {
            pOutputPortNegotiationData->numInputPortsNotification++;
            if (pOutputPortNegotiationData->numInputPortsNotification == pOutputPort->numInputPortsConnected)
            {
                m_bufferNegotiationData.numOutputPortsNotified++;
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetTuningDataManager
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TuningDataManager* Node::GetTuningDataManager()
{
    return m_pPipeline->GetTuningDataManager();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetTuningDataManagerWithCameraId
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
TuningDataManager* Node::GetTuningDataManagerWithCameraId(
    UINT32 cameraId)
{
    return HwEnvironment::GetInstance()->GetTuningDataManager(cameraId);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetIntraPipelinePerFramePool
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MetadataPool* Node::GetIntraPipelinePerFramePool(
    PoolType poolType,
    UINT     intraPipelineId)
{
    return m_pPipeline->GetIntraPipelinePerFramePool(poolType, intraPipelineId);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::FillPipelineInputOptions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT Node::FillPipelineInputOptions(
    ChiPipelineInputOptions* pPipelineInputOptions)
{
    UINT numInputs = 0;

    if (Sensor == Type())
    {
        /// @note Sensor does not have input ports so the requirements are kept on the output ports
        numInputs = m_outputPortsData.numPorts;

        for (UINT output = 0; output < m_outputPortsData.numPorts; output++)
        {
            if (SensorOutputPort0 != m_outputPortsData.pOutputPorts[output].portId)
            {
                // Decrement for the skipped sensor node output port.
                numInputs--;
                continue;
            }
            OutputPortNegotiationData* pOutputPortNegotiationData = &m_bufferNegotiationData.pOutputPortNegotiationData[output];
            BufferRequirement*         pInputPortBuffer           = &pOutputPortNegotiationData->inputPortRequirement[0];
            UINT                       outputPortIndex            = pOutputPortNegotiationData->outputPortIndex;
            ChiBufferOptions*          pChiInputOptions           = &pPipelineInputOptions->bufferOptions;

            pPipelineInputOptions->nodePort.nodeId         = Type();
            pPipelineInputOptions->nodePort.nodeInstanceId = InstanceID();
            pPipelineInputOptions->nodePort.nodePortId     = m_outputPortsData.pOutputPorts[outputPortIndex].portId;

            pChiInputOptions->size                     = sizeof(ChiBufferOptions);
            pChiInputOptions->minDimension.width       = pInputPortBuffer->minWidth;
            pChiInputOptions->minDimension.height      = pInputPortBuffer->minHeight;
            pChiInputOptions->maxDimension.width       = pInputPortBuffer->maxWidth;
            pChiInputOptions->maxDimension.height      = pInputPortBuffer->maxHeight;
            pChiInputOptions->optimalDimension.width   = pInputPortBuffer->optimalWidth;
            pChiInputOptions->optimalDimension.height  = pInputPortBuffer->optimalHeight;

            pPipelineInputOptions++;
        }
    }
    else
    {
        numInputs = 0;

        for (UINT input = 0; input < m_inputPortsData.numPorts; input++)
        {
            if (TRUE == IsSourceBufferInputPort(input))
            {
                PortBufferOptions* pInputBufferOptions = &m_bufferNegotiationData.inputBufferOptions[input];
                BufferRequirement* pInputPortBuffer    = &pInputBufferOptions->bufferRequirement;
                ChiBufferOptions*  pChiInputOptions    = &pPipelineInputOptions->bufferOptions;

                pPipelineInputOptions->nodePort.nodeId         = pInputBufferOptions->nodeId;
                pPipelineInputOptions->nodePort.nodeInstanceId = pInputBufferOptions->instanceId;
                pPipelineInputOptions->nodePort.nodePortId     = pInputBufferOptions->portId;

                pChiInputOptions->size                     = sizeof(ChiBufferOptions);
                pChiInputOptions->minDimension.width       = pInputPortBuffer->minWidth;
                pChiInputOptions->minDimension.height      = pInputPortBuffer->minHeight;
                pChiInputOptions->maxDimension.width       = pInputPortBuffer->maxWidth;
                pChiInputOptions->maxDimension.height      = pInputPortBuffer->maxHeight;
                pChiInputOptions->optimalDimension.width   = pInputPortBuffer->optimalWidth;
                pChiInputOptions->optimalDimension.height  = pInputPortBuffer->optimalHeight;

                pPipelineInputOptions++;
                numInputs++;
            }
        }
    }

    return numInputs;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::EarlyReturnTag
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL Node::EarlyReturnTag(
    UINT64       requestId,
    UINT32       tag,
    const VOID*  pData,
    SIZE_T       size)
{
    BOOL earlyReturn = FALSE;
    static const UINT SizeOfFixedList = 13;
    switch (tag)
    {
        case ControlAWBState:
        case ControlAFState:
        case ControlAEState:
        case ControlAELock:
        case ControlAEExposureCompensation:
        case ControlAFMode:
        case ControlAWBMode:
        case ControlAETargetFpsRange:
        case ControlAERegions:
        case ControlAFRegions:
        case ControlMode:
        case FlashMode:
        case FlashState:
            {
                MetadataSlot* pSlot = NULL;
                pSlot = m_pEarlyMainPool->GetSlot(requestId);
                pSlot->SetMetadataByTag(tag, pData, size);

                m_pEarlyMainPool->IncCurrentEarlyTagsCountForSlot(requestId);

                if (m_pEarlyMainPool->GetCurrentEarlyTagsCountForSlot(requestId) >= SizeOfFixedList)
                {
                    ProcessEarlyMetadataDone(requestId);

                    m_pEarlyMainPool->ResetCurrentEarlyTagsCountForSlot(requestId);
                }
                break;
            }
        default:
            break;
    }

    return earlyReturn;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::NotifyOutputConsumed
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::NotifyOutputConsumed(
    UINT   outputPortIndex,
    UINT64 requestId)
{
    m_pBufferReleaseLock->Lock();

    OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[outputPortIndex];

    if (pOutputPort->bufferProperties.maxImageBuffers > 0)
    {
        UINT imageFenceIndex = requestId % pOutputPort->bufferProperties.maxImageBuffers;

        if (NULL != pOutputPort->ppImageBuffers[imageFenceIndex])
        {
            if (0 == (pOutputPort->ppImageBuffers[imageFenceIndex]->ReleaseBufferManagerImageReference()))
            {
                m_pFenceCreateReleaseLock->Lock();

                if (TRUE == IsNonSinkHALBufferOutput(outputPortIndex))
                {
                    pOutputPort->ppImageBuffers[imageFenceIndex]->Release();
                }

                if (CSLInvalidHandle != pOutputPort->pFenceHandlerData[imageFenceIndex].hFence)
                {
                    CamxResult result = CSLReleaseFence(pOutputPort->pFenceHandlerData[imageFenceIndex].hFence);

                    CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d), result: %d requestID:%llu ImgBuf:%p",
                        this, m_pNodeName, InstanceID(), &pOutputPort->pFenceHandlerData[imageFenceIndex].hFence,
                        pOutputPort->pFenceHandlerData[imageFenceIndex].hFence, result, requestId,
                        pOutputPort->ppImageBuffers[imageFenceIndex]);

                    pOutputPort->pFenceHandlerData[imageFenceIndex].hFence = CSLInvalidHandle;
                }

                if (CSLInvalidHandle != pOutputPort->pDelayedBufferFenceHandlerData[imageFenceIndex].hFence)
                {
                    CSLReleaseFence(pOutputPort->pDelayedBufferFenceHandlerData[imageFenceIndex].hFence);

                    CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d) reqID:%llu",
                        this, m_pNodeName, InstanceID(), &pOutputPort->pFenceHandlerData[imageFenceIndex].hFence,
                        pOutputPort->pFenceHandlerData[imageFenceIndex].hFence, requestId);

                    pOutputPort->pDelayedBufferFenceHandlerData[imageFenceIndex].hFence = CSLInvalidHandle;
                }
                m_pFenceCreateReleaseLock->Unlock();
                pOutputPort->ppImageBuffers[imageFenceIndex] = NULL;
            }
        }
    }

    if (TRUE == IsBypassableNode())
    {
        for (UINT index = 0; index < pOutputPort->numSourcePortsMapped; index++)
        {
            UINT32 portId = pOutputPort->pMappedSourcePortIds[index];
            for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
            {
                InputPort* pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];
                if (portId == pInputPort->portId)
                {
                    NotifyInputConsumed(inputPortIndex, requestId);
                }
            }
        }
    }
    m_pBufferReleaseLock->Unlock();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::NotifyInputConsumed
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::NotifyInputConsumed(
    UINT   inputPortIndex,
    UINT64 requestId)
{
    InputPort* pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];

    if (FALSE == IsSourceBufferInputPort(inputPortIndex))
    {
        pInputPort->pParentNode->NotifyOutputConsumed(pInputPort->parentOutputPortIndex, requestId);
    }
    else
    {
        m_pFenceCreateReleaseLock->Lock();
        UINT         requestIdIndex = requestId % MaxRequestQueueDepth;
        ImageBuffer* pImageBuffer   = pInputPort->ppImageBuffers[requestIdIndex];

        CAMX_ASSERT(NULL != pImageBuffer);

        // don't immediately release the hal buffer as it can be used by child node
        // when node is bypassed
        if ((TRUE == pImageBuffer->HasBackingBuffer()))
        {
            pImageBuffer->Release();

            CAMX_ASSERT(CSLInvalidHandle != pInputPort->phFences[requestIdIndex]);

            CamxResult result = CSLReleaseFence(pInputPort->phFences[requestIdIndex]);

            CAMX_LOG_DRQ("ReleaseFence...Node: %08x, id: %s:%d, fence: %08x(%d), result: %d reqID:%llu",
                this, m_pNodeName, InstanceID(), &pInputPort->phFences[requestIdIndex],
                pInputPort->phFences[requestIdIndex], result, requestId);
            CAMX_ASSERT(result == CamxResultSuccess);

            pInputPort->phFences[requestIdIndex] = CSLInvalidHandle;
        }
        m_pFenceCreateReleaseLock->Unlock();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetNumOfBuffersToBeAllocatedImmediately
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT Node::GetNumOfBuffersToBeAllocatedImmediately(
    UINT immediateBufferCount,
    UINT maxBufferCount)
{
    UINT nNumOfBuffers = immediateBufferCount;

    // Ensure that the immediateBufferCount is not greater than the max number of buffers
    if (nNumOfBuffers > maxBufferCount)
    {
        nNumOfBuffers = maxBufferCount;
    }

    CAMX_LOG_VERBOSE(CamxLogGroupCore, "Preallocating %d buffers for node %s, instance = %d",
                     nNumOfBuffers, Name(), InstanceID());

    return nNumOfBuffers;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetMaximumNumOfBuffersToBeAllocated
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
UINT Node::GetMaximumNumOfBuffersToBeAllocated(
    UINT maxBufferCount)
{
    UINT nMaxNumOfBuffers = maxBufferCount;

    const StaticSettings* pSettings = GetStaticSettings();
    CAMX_ASSERT(NULL != pSettings);

    if (NULL != pSettings)
    {
        if (TRUE == IsRealTime())
        {
            // If the max image buffer count override value for real-time nodes is set to a number smaller than
            // the maximum possible size, the maximum number of buffers to be allocated is updated accordingly
            if (static_cast<UINT>(-1) != pSettings->overrideMaxImageBufferCountRealTime)
            {
                nMaxNumOfBuffers = pSettings->overrideMaxImageBufferCountRealTime;

                // Ensure that the max number of buffers allocated is not greater than the value in the buffer property setting
                if (nMaxNumOfBuffers > maxBufferCount)
                {
                    nMaxNumOfBuffers = maxBufferCount;
                }

                // Ensure that the max number of buffers allocated is greater than the max pipeline delay
                if (nMaxNumOfBuffers <= pSettings->maxPipelineDelay)
                {
                    nMaxNumOfBuffers = pSettings->maxPipelineDelay + 1;
                }
            }
        }
        else
        {
            // If the max image buffer count override value for non-real time nodes is set to a number smaller than
            // the maximum possible size, the maximum number of buffers to be allocated is updated accordingly
            if (static_cast<UINT>(-1) != pSettings->overrideMaxImageBufferCountNonRealTime)
            {
                nMaxNumOfBuffers = pSettings->overrideMaxImageBufferCountNonRealTime;

                // Ensure that the max number of buffers allocated is not greater than the value in the buffer property setting
                if (nMaxNumOfBuffers > maxBufferCount)
                {
                    nMaxNumOfBuffers = maxBufferCount;
                }
            }
        }
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "NULL pointer check for static settings failed");
    }

    CAMX_LOG_VERBOSE(CamxLogGroupCore, "Maximum %d buffers will be allocated for node %s instance = %d",
        nMaxNumOfBuffers, Name(), InstanceID());

    return nMaxNumOfBuffers;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::GetStaticSettings
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const StaticSettings* Node::GetStaticSettings() const
{
    return HwEnvironment::GetInstance()->GetStaticSettings();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::SetInputPortBufferDelta
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::SetInputPortBufferDelta(
    UINT    portIndex,
    UINT64  delta)
{
    if (portIndex < m_inputPortsData.numPorts)
    {
        m_inputPortsData.pInputPorts[portIndex].bufferDelta = delta;
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Invalid port index %u", portIndex);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::UpdateBufferInfoforPendingInputPorts
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::UpdateBufferInfoforPendingInputPorts(
    PerRequestActivePorts* pRequestPorts)
{
    CamxResult result = CamxResultSuccess;

    for (UINT8 index = 0; index < pRequestPorts->numInputPorts; index++)
    {
        if (TRUE == pRequestPorts->pInputPorts[index].flags.isPendingBuffer)
        {
            DelayedOutputBufferInfo* pDelayedBufferData = pRequestPorts->pInputPorts[index].pDelayedOutputBufferData;

            if (NULL != pDelayedBufferData)
            {
                pRequestPorts->pInputPorts[index].pImageBuffer = pDelayedBufferData->pImageBuffer[0];
                pRequestPorts->pInputPorts[index].phFence      = &pDelayedBufferData->hFence;

                pRequestPorts->pInputPorts[index].flags.isParentInputBuffer = pDelayedBufferData->isParentInputBuffer;
                pRequestPorts->pInputPorts[index].flags.isPendingBuffer     = FALSE;
            }
            else
            {
                CAMX_LOG_DRQ("%s Invalid delayed buffer data request = %llu", m_pNodeName, m_tRequestId);
            }
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::SetBufferDependency
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::SetBufferDependency(
    ExecuteProcessRequestData* pExecuteProcessRequestData)
{
    CamxResult result = CamxResultSuccess;

    PerRequestActivePorts*  pEnabledPorts = pExecuteProcessRequestData->pEnabledPortsInfo;
    NodeProcessRequestData* pNodeRequestData = pExecuteProcessRequestData->pNodeProcessRequestData;

    CAMX_ASSERT_MESSAGE(NULL != pNodeRequestData, "Node capture request data NULL pointer");
    CAMX_ASSERT_MESSAGE(NULL != pEnabledPorts, "Per Request Active Ports NULL pointer");

    for (UINT portIndex = 0; portIndex < pEnabledPorts->numInputPorts; portIndex++)
    {
        PerRequestInputPortInfo* pPerRequestInputPort = &pEnabledPorts->pInputPorts[portIndex];
        CAMX_ASSERT_MESSAGE(NULL != pPerRequestInputPort, "Per Request Input Port NULL pointer");

        if ((0    == CamxAtomicLoadU(pPerRequestInputPort->pIsFenceSignaled)) &&
            (TRUE == pPerRequestInputPort->flags.isPendingBuffer))
        {
            UINT fenceCount = pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount;

            pNodeRequestData->dependencyInfo[0].bufferDependency.phFences[fenceCount] =
                pPerRequestInputPort->phFence;
            pNodeRequestData->dependencyInfo[0].bufferDependency.pIsFenceSignaled[fenceCount] =
                pPerRequestInputPort->pIsFenceSignaled;

            CAMX_LOG_DRQ("Setting buffer dependency for Node %s with fence %x for request= %llu",
                m_pNodeName,
                *pPerRequestInputPort->phFence,
                pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId);

            pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount++;
        }
    }

    if (pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount > 0)
    {
        pNodeRequestData->dependencyInfo[0].dependencyFlags.hasBufferDependency = TRUE;
        pNodeRequestData->numDependencyLists = 1;
        pNodeRequestData->dependencyInfo[0].processSequenceId = 1;

        CAMX_LOG_VERBOSE(CamxLogGroupCore,
            "Added buffer dependencies. reqId = %llu Number of fences = %d",
            pExecuteProcessRequestData->pNodeProcessRequestData->pCaptureRequest->requestId,
            pNodeRequestData->dependencyInfo[0].bufferDependency.fenceCount);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::UpdateDeviceIndices
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::UpdateDeviceIndices(
    UINT         outputPortIndex,
    const INT32* pDeviceIndices,
    UINT         deviceCount)
{
    CAMX_ASSERT(outputPortIndex < m_outputPortsData.numPorts);
    CAMX_ASSERT(NULL != pDeviceIndices);
    CAMX_ASSERT(0 != deviceCount);

    CAMX_LOG_VERBOSE(CamxLogGroupCore, "Update Dependencies output port index = %d", outputPortIndex);
    for (UINT count = 0; count < deviceCount; count++)
    {
        BOOL matchFound = FALSE;
        UINT curDeviceIndex = m_outputPortsData.pOutputPorts[outputPortIndex].deviceCount;
        const INT32* pOutputPortDeviceIndices = &m_outputPortsData.pOutputPorts[outputPortIndex].deviceIndices[0];

        // Check duplicate entry. If the device index is already present, don't add
        for (UINT deviceIndex = 0; deviceIndex < curDeviceIndex; deviceIndex++)
        {
            if (pOutputPortDeviceIndices[deviceIndex] == pDeviceIndices[count])
            {
                matchFound = TRUE;
                break;
            }
        }

        if (FALSE == matchFound)
        {
            m_outputPortsData.pOutputPorts[outputPortIndex].deviceIndices[curDeviceIndex] = pDeviceIndices[count];
            m_outputPortsData.pOutputPorts[outputPortIndex].deviceCount++;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::BypassNodeProcessing
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::BypassNodeProcessing()
{
    // If the node is a bypassable node then it's parent need to be informed with the node device indices that bypassable nodes'
    // output is connected to, inorder to do the buffer mapping
    CAMX_ASSERT(TRUE == IsBypassableNode());

    OutputPort* pOutputPort = NULL;
    INT32       deviceIndices[CamxMaxDeviceIndex] = { 0 };
    UINT        deviceIndicesCount = 0;
    BOOL        matchFound = FALSE;

    for (UINT outputPortIndex = 0; outputPortIndex < m_outputPortsData.numPorts; outputPortIndex++)
    {
        pOutputPort = &m_outputPortsData.pOutputPorts[outputPortIndex];

        if (0 < pOutputPort->numSourcePortsMapped)
        {
            for (UINT index = 0; index < pOutputPort->deviceCount; index++)
            {
                for (UINT deviceIndex = 0; deviceIndex < deviceIndicesCount; deviceIndex++)
                {
                    if (deviceIndices[deviceIndex] == pOutputPort->deviceIndices[index])
                    {
                        matchFound = TRUE;
                    }
                }
                // if no match found then add device index to the list
                if ((FALSE == matchFound) && ((deviceIndicesCount + 1) < CamxMaxDeviceIndex))
                {
                    deviceIndices[deviceIndicesCount++] = pOutputPort->deviceIndices[index];
                }
            }
        }

        for (UINT index = 0; index < pOutputPort->numSourcePortsMapped; index++)
        {
            UINT32 portId = pOutputPort->pMappedSourcePortIds[index];

            // optimize input port index search
            for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
            {
                InputPort* pInputPort = &m_inputPortsData.pInputPorts[inputPortIndex];
                if ((portId == pInputPort->portId) && (NULL != pInputPort->pParentNode) &&
                    (FALSE == IsSourceBufferInputPort(inputPortIndex)))
                {
                    pInputPort->pParentNode->UpdateDeviceIndices(pInputPort->parentOutputPortIndex,
                        pOutputPort->deviceIndices, pOutputPort->deviceCount);
                    pInputPort->pParentNode->AddInputConnectedforBypass(pInputPort->parentOutputPortIndex,
                        (pOutputPort->numInputPortsConnected + pOutputPort->numInputPortsConnectedinBypass));
                }
            }
        }
    }

    for (UINT inputPortIndex = 0; inputPortIndex < m_inputPortsData.numPorts; inputPortIndex++)
    {
        // Add device indices of the child node for bypassable node if any of the input port is
        // source buffer port
        if (TRUE == IsSourceBufferInputPort(inputPortIndex))
        {
            for (UINT index = 0; index < deviceIndicesCount; index++)
            {
                CAMX_ASSERT_MESSAGE((m_deviceIndexCount + 1) < CamxMaxDeviceIndex, "Device index is out of bound");
                m_deviceIndices[m_deviceIndexCount++] = deviceIndices[index];
            }
            break;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::DumpState
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::DumpState(
    INT     fd,
    UINT32  indent,
    UINT64  requestId)
{

    NodePerRequestInfo* pInfo = &m_perRequestInfo[requestId % MaxRequestQueueDepth];

    if (pInfo->requestId == requestId)
    {
        CAMX_LOG_TO_FILE(fd, indent, "%s:  m_nodeFlags: %08x", m_nodeNameInstanceId, m_nodeFlags);
        CAMX_LOG_TO_FILE(fd, indent + 2, "numUnsignaledFences: %d", pInfo->numUnsignaledFences);
        CAMX_LOG_TO_FILE(fd, indent + 2, "Active port details:");

        for (UINT i = 0; i < pInfo->activePorts.numInputPorts; i++)
        {
            CAMX_LOG_TO_FILE(fd, indent + 4, "In::port: %d  flags: %08x",
                pInfo->activePorts.pInputPorts[i].portId,
                pInfo->activePorts.pInputPorts[i].flags.allFlags);
            CAMX_LOG_TO_FILE(fd, indent + 6, "ImageBuffer fence: %p(%08X)  fenceSignaled: %d",
                pInfo->activePorts.pInputPorts[i].phFence,
                (pInfo->activePorts.pInputPorts[i].phFence != NULL) ?
                    *(pInfo->activePorts.pInputPorts[i].phFence) : 0,
                (pInfo->activePorts.pInputPorts[i].pIsFenceSignaled != NULL) ?
                    *(pInfo->activePorts.pInputPorts[i].pIsFenceSignaled) : 0);
            if (TRUE == pInfo->activePorts.pInputPorts[i].flags.isPendingBuffer)
            {
                CAMX_LOG_TO_FILE(fd, indent + 6, "DelayedOutputBuffer fence: (%08x)",
                    pInfo->activePorts.pInputPorts[i].pDelayedOutputBufferData->hFence);
            }
        }
        for (UINT i = 0; i < pInfo->activePorts.numOutputPorts; i++)
        {
            CAMX_LOG_TO_FILE(fd, indent + 4, "Out::port: %d  flags: %08x",
                pInfo->activePorts.pOutputPorts[i].portId,
                pInfo->activePorts.pOutputPorts[i].flags.allFlags);
            CAMX_LOG_TO_FILE(fd, indent + 6, "ImageBuffer fence: %p(%08x)  fenceSignaled: %d  numBuffers: %d",
                pInfo->activePorts.pOutputPorts[i].phFence,
                (pInfo->activePorts.pOutputPorts[i].phFence != NULL) ?
                    *(pInfo->activePorts.pOutputPorts[i].phFence) : 0,
                (pInfo->activePorts.pOutputPorts[i].pIsFenceSignaled != NULL) ?
                    *(pInfo->activePorts.pOutputPorts[i].pIsFenceSignaled) : 0,
                pInfo->activePorts.pOutputPorts[i].numOutputBuffers);
            if (TRUE == pInfo->activePorts.pOutputPorts[i].flags.isDelayedBuffer)
            {
                CAMX_LOG_TO_FILE(fd, indent + 6, "DelayedOutputBuffer fence: (%08x)",
                    pInfo->activePorts.pOutputPorts[i].pDelayedOutputBufferData->hFence);
            }
        }
    }
    else
    {
        // If the requestId doesn't match, nothing to do...it's gone from the Node...
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::DumpNodeInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::DumpNodeInfo(
    INT     fd,
    UINT32  indent)
{
    for (UINT i = 0; i < m_inputPortsData.numPorts; i++)
    {
        CAMX_LOG_TO_FILE(fd, indent, "In::%s::port%d  format: %08x  portDisabled: %d  bufferDelta: %llu",
            m_nodeNameInstanceId,
            m_inputPortsData.pInputPorts[i].portId,
            m_inputPortsData.pInputPorts[i].imageFormat,
            m_inputPortsData.pInputPorts[i].portDisabled,
            m_inputPortsData.pInputPorts[i].bufferDelta);
    }
    for (UINT i = 0; i < m_outputPortsData.numPorts; i++)
    {
        CAMX_LOG_TO_FILE(fd, indent, "Out::%s::port%d  format: %08x  maxNumBuffers: %d  memFlags: %08x  heap: %d",
            m_nodeNameInstanceId,
            m_outputPortsData.pOutputPorts[i].portId,
            m_outputPortsData.pOutputPorts[i].bufferProperties.imageFormat,
            m_outputPortsData.pOutputPorts[i].bufferProperties.maxImageBuffers,
            m_outputPortsData.pOutputPorts[i].bufferProperties.memFlags,
            m_outputPortsData.pOutputPorts[i].bufferProperties.bufferHeap);
        CAMX_LOG_TO_FILE(fd, indent + 2, "flags: %08x  streamEnableMask: %08x  numBatchedFrames: %d",
            m_outputPortsData.pOutputPorts[i].flags,
            m_outputPortsData.pOutputPorts[i].enabledInStreamMask,
            m_outputPortsData.pOutputPorts[i].numBatchedFrames);
        for (UINT j = 0; j < m_outputPortsData.pOutputPorts[i].deviceCount; j++)
        {
            CAMX_LOG_TO_FILE(fd, indent + 2, "deviceIndex[%d]: %d",
                j, m_outputPortsData.pOutputPorts[i].deviceIndices[j]);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::DumpLinkInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::DumpLinkInfo(
    INT     fd,
    UINT32  indent)
{
    for (UINT i = 0; i < m_inputPortsData.numPorts; i++)
    {
        if (TRUE == m_inputPortsData.pInputPorts[i].flags.isSourceBuffer)
        {
            CAMX_LOG_TO_FILE(fd, indent, "Buffer%d -> In::%s::port%d",
                m_inputPortsData.pInputPorts[i].sourceTargetStreamId,
                m_nodeNameInstanceId,
                m_inputPortsData.pInputPorts[i].portId);
        }
        else
        {
            Node* pParent = m_inputPortsData.pInputPorts[i].pParentNode;
            UINT port = m_inputPortsData.pInputPorts[i].parentOutputPortIndex;

            CAMX_LOG_TO_FILE(fd, indent, "Out::%s::port%d -> In::%s::port%d",
                pParent->m_nodeNameInstanceId,
                pParent->m_outputPortsData.pOutputPorts[port].portId,
                m_nodeNameInstanceId,
                m_inputPortsData.pInputPorts[i].portId);
        }
    }
    for (UINT i = 0; i < m_outputPortsData.numPorts; i++)
    {
        // Every node walked, and links generated from inputs.  Only need to capture sink output ports
        if (TRUE == m_outputPortsData.pOutputPorts[i].flags.isSinkBuffer)
        {
            CAMX_LOG_TO_FILE(fd, indent, "Out::%s::port%d -> SinkBuffer%d",
                m_nodeNameInstanceId,
                m_outputPortsData.pOutputPorts[i].portId,
                m_outputPortsData.pOutputPorts[i].sinkTargetStreamId);
        }
        else if (TRUE == m_outputPortsData.pOutputPorts[i].flags.isSinkNoBuffer)
        {
            CAMX_LOG_TO_FILE(fd, indent, "Out::%s::port%d -> SinkNoBuffer",
                m_nodeNameInstanceId,
                m_outputPortsData.pOutputPorts[i].portId);
        }
    }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Node::GetMultiCameraInfo
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::GetMultiCameraInfo(
    BOOL*   pIsMultiCameraUsecase,
    UINT32* pNumberOfCamerasRunning,
    BOOL*   pIsMasterCamera)
{
    BOOL       isMultiCameraUsecase     = FALSE;
    UINT32     numberOfCamerasRunning   = 1;
    BOOL       isMasterCamera           = TRUE;
    CamxResult result                   = CamxResultSuccess;
    UINT32     cameraIdTag              = 0;
    UINT32     masterCameraTag          = 0;
    UINT32     lpmModeTag               = 0;

    if ((NULL == pIsMultiCameraUsecase) && (NULL == pNumberOfCamerasRunning) && (NULL == pIsMasterCamera))
    {
        CAMX_LOG_ERROR(CamxLogGroupCore, "Invalid input args");
        result = CamxResultEFailed;
    }

    if (CamxResultSuccess == result)
    {
        result = VendorTagManager::QueryVendorTagLocation("com.qti.chi.multicamerainfo", "MultiCameraIdRole", &cameraIdTag);

        result |= VendorTagManager::QueryVendorTagLocation("com.qti.chi.multicamerainfo", "MasterCamera", &masterCameraTag);

        result |= VendorTagManager::QueryVendorTagLocation("com.qti.chi.multicamerainfo", "LowPowerMode", &lpmModeTag);

        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupCore,
                           "Node[%s] Instance[%d] : MultiCamera vendor tags location not available %d %d %d",
                           Name(), InstanceID(), cameraIdTag, masterCameraTag, lpmModeTag);
        }
    }

    if (CamxResultSuccess == result)
    {
        cameraIdTag     |= InputMetadataSectionMask;
        masterCameraTag |= InputMetadataSectionMask;
        lpmModeTag      |= InputMetadataSectionMask;

        MultiCameraIdRole*  pInputMetadata      = NULL;
        UINT32*             pIsMaster           = NULL;
        LowPowerModeInfo*   pLPMInfo            = NULL;
        const UINT          propertyTag[3]      = { cameraIdTag, masterCameraTag, lpmModeTag };
        VOID*               pData[3]            = { 0 };
        UINT64              propertyOffset[3]   = { 0 };
        UINT                length              = CAMX_ARRAY_SIZE(propertyTag);

        GetDataList(propertyTag, pData, propertyOffset, length);

        if ((NULL != pData[0]) && (NULL != pData[1]) && (NULL != pData[2]))
        {
            pInputMetadata      = static_cast<MultiCameraIdRole*>(pData[0]);
            pIsMaster           = static_cast<UINT32*>(pData[1]);
            pLPMInfo            = static_cast<LowPowerModeInfo*>(pData[2]);

            CAMX_LOG_VERBOSE(CamxLogGroupCore, "Node[%s] Instance[%d] : currentCameraRole = %d, currentCameraId=%d, "
                             "logicalCameraId=%d, masterCameraRole=%d",
                             Name(), InstanceID(),
                             pInputMetadata->currentCameraRole, pInputMetadata->currentCameraId,
                             pInputMetadata->logicalCameraId,   pInputMetadata->masterCameraRole);

            CAMX_LOG_VERBOSE(CamxLogGroupCore, "Node[%s] Instance[%d] : isMasterCamera=%d",
                             Name(), InstanceID(), *pIsMaster);

            CAMX_LOG_VERBOSE(CamxLogGroupCore, "Node[%s] Instance[%d] : isLPMEnabled = %d, isSlaveOperational=%d",
                             Name(), InstanceID(), pLPMInfo->isLPMEnabled, pLPMInfo->isSlaveOperational);

            switch (pInputMetadata->currentCameraRole)
            {
                case CameraRoleTypeWide:
                case CameraRoleTypeTele:
                    isMultiCameraUsecase    = TRUE;
                    isMasterCamera          = static_cast<BOOL>(*pIsMaster);
                    break;
                case CameraRoleTypeDefault:
                default:
                    isMultiCameraUsecase    = FALSE;
                    isMasterCamera          = TRUE;
                    break;
            }

            if (TRUE == isMultiCameraUsecase)
            {
                if (FALSE == isMasterCamera)
                {
                    // EPR coming to aux, there will be an EPR on master as well
                    numberOfCamerasRunning = 2;
                }
                else
                {
                    if (FALSE == pLPMInfo->isLPMEnabled)
                    {
                        numberOfCamerasRunning = 2;
                    }
                    else
                    {
                        if (TRUE == pLPMInfo->isSlaveOperational)
                        {
                            numberOfCamerasRunning = 2;
                        }
                    }
                }
            }
        }
        else
        {
            CAMX_LOG_ERROR(CamxLogGroupCore, "Node[%s] Instance[%d] : MultiCamera vendor tags data not available %p %dp %p",
                          Name(), InstanceID(), pData[0], pData[1], pData[2]);
        }
    }

    CAMX_LOG_VERBOSE(CamxLogGroupCore,
                     "Node[%s] Instance[%d] : isMultiCameraUsecase = %d, numberOfCamerasRunning=%d, isMasterCamera=%d",
                     Name(), InstanceID(), isMultiCameraUsecase, numberOfCamerasRunning, isMasterCamera);

    if (NULL != pIsMultiCameraUsecase)
    {
        *pIsMultiCameraUsecase = isMultiCameraUsecase;
    }

    if (NULL != pNumberOfCamerasRunning)
    {
        *pNumberOfCamerasRunning = numberOfCamerasRunning;
    }

    if (NULL != pIsMasterCamera)
    {
        *pIsMasterCamera = isMasterCamera;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::ReleaseImageBuffers
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult Node::ReleaseImageBuffers()
{
    CamxResult result = CamxResultSuccess;

    for (UINT outputPortIndex = 0; outputPortIndex < m_outputPortsData.numPorts; outputPortIndex++)
    {
        OutputPort* pOutputPort = &m_outputPortsData.pOutputPorts[outputPortIndex];
        if (NULL != pOutputPort->pImageBufferManager)
        {
            result = pOutputPort->pImageBufferManager->ReleaseBuffers(TRUE);
        }
        if (CamxResultSuccess != result)
        {
            CAMX_LOG_ERROR(CamxLogGroupCore, "%s ReleaseBuffers failed.", Name());
            break;
        }
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Node::AddInputConnectedforBypass
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID Node::AddInputConnectedforBypass(
    UINT outputPortIndex,
    UINT numInputPortsConnected)
{
    CAMX_ASSERT(outputPortIndex < m_outputPortsData.numPorts);

    CAMX_LOG_VERBOSE(CamxLogGroupCore, "Node %s: %d numInputPorts %d", m_pNodeName,
        InstanceID(), numInputPortsConnected);

    m_outputPortsData.pOutputPorts[outputPortIndex].numInputPortsConnectedinBypass = numInputPortsConnected;
}

CAMX_NAMESPACE_END
